diff --git a/.travis.yml b/.travis.yml index 183d22e57..3dbe1e066 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,4 +45,4 @@ script: - mysql -uzmuser -pzmpass < db/zm_create.sql - mysql -uzmuser -pzmpass zm < db/test.monitor.sql - sudo zmpkg.pl start - - sudo zmfilter.pl -f purgewhenfull + - sudo zmfilter.pl --filter purgewhenfull diff --git a/CMakeLists.txt b/CMakeLists.txt index 5379f061d..7f659de3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,6 +401,59 @@ else(MYSQLCLIENT_LIBRARIES) "ZoneMinder requires mysqlclient but it was not found on your system") endif(MYSQLCLIENT_LIBRARIES) +# x264 (using find_library and find_path) +find_library(X264_LIBRARIES x264) +if(X264_LIBRARIES) + set(HAVE_LIBX264 1) + list(APPEND ZM_BIN_LIBS "${X264_LIBRARIES}") + find_path(X264_INCLUDE_DIR x264.h) + if(X264_INCLUDE_DIR) + include_directories("${X264_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${X264_INCLUDE_DIR}") + endif(X264_INCLUDE_DIR) + mark_as_advanced(FORCE X264_LIBRARIES X264_INCLUDE_DIR) + check_include_files("stdint.h;x264.h" HAVE_X264_H) + set(optlibsfound "${optlibsfound} x264") +else(X264_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} x264") +endif(X264_LIBRARIES) + +# mp4v2 (using find_library and find_path) +find_library(MP4V2_LIBRARIES mp4v2) +if(MP4V2_LIBRARIES) + set(HAVE_LIBMP4V2 1) + list(APPEND ZM_BIN_LIBS "${MP4V2_LIBRARIES}") + + # mp4v2/mp4v2.h + find_path(MP4V2_INCLUDE_DIR mp4v2/mp4v2.h) + if(MP4V2_INCLUDE_DIR) + include_directories("${MP4V2_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") + endif(MP4V2_INCLUDE_DIR) + check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H) + + # mp4v2.h + find_path(MP4V2_INCLUDE_DIR mp4v2.h) + if(MP4V2_INCLUDE_DIR) + include_directories("${MP4V2_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") + endif(MP4V2_INCLUDE_DIR) + check_include_file("mp4v2.h" HAVE_MP4V2_H) + + # mp4.h + find_path(MP4V2_INCLUDE_DIR mp4.h) + if(MP4V2_INCLUDE_DIR) + include_directories("${MP4V2_INCLUDE_DIR}") + set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") + endif(MP4V2_INCLUDE_DIR) + check_include_file("mp4.h" HAVE_MP4_H) + + mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR) + set(optlibsfound "${optlibsfound} mp4v2") +else(MP4V2_LIBRARIES) + set(optlibsnotfound "${optlibsnotfound} mp4v2") +endif(MP4V2_LIBRARIES) + set(PATH_FFMPEG "") set(OPT_FFMPEG "no") # Do not check for ffmpeg if ZM_NO_FFMPEG is on diff --git a/README.md b/README.md index 42a570cc1..ccaccbd06 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ -ZoneMinder -========== +ZoneMinder H264 Patch -[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) +[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png?branch=feature-h264-videostorage)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) + +##Feature-h264-videostorage Branch Details +This branch supports direct recording of h264 cameras into MP4 format uisng the h264 Passthrough option, but only with FFMPEG Monitors currently. It also provides h264 encoding for any other monitor type. If you encounter any issues, please open an issue on GitHub and attach it to the h264 milestone. But do remember this is bleeding edge so it will have problems. +Thanks to @chriswiggins and @mastertheknife for their work, @SteveGilvarry is now maintaining this branch and welcomes any assistance. + +**The following SQL changes are required, these will be merged to zmupdate once we are ready to merge this branch to master.** +``` +ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing` , +ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs` , +ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter` ; + +ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames` ; +``` All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 99c855392..130279e35 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -193,6 +193,7 @@ CREATE TABLE `Events` ( `Length` decimal(10,2) NOT NULL default '0.00', `Frames` int(10) unsigned default NULL, `AlarmFrames` int(10) unsigned default NULL, + `DefaultVideo` VARCHAR( 64 ) NOT NULL, `TotScore` int(10) unsigned NOT NULL default '0', `AvgScore` smallint(5) unsigned default '0', `MaxScore` smallint(5) unsigned default '0', @@ -346,6 +347,10 @@ CREATE TABLE `Monitors` ( `Palette` int(10) unsigned NOT NULL default '0', `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `Deinterlacing` int(10) unsigned NOT NULL default '0', + `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , + `VideoWriter` TINYINT NOT NULL DEFAULT '0', + `EncoderParameters` TEXT NOT NULL, + `RecordAudio` TINYINT NOT NULL DEFAULT '0', `RTSPDescribe` tinyint(1) unsigned NOT NULL default '0', `Brightness` mediumint(7) NOT NULL default '-1', `Contrast` mediumint(7) NOT NULL default '-1', @@ -579,7 +584,7 @@ insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edi -- -- Add a sample filter to purge the oldest 100 events when the disk is 95% full -- -insert into Filters values ('PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0,0,0,0,0,0,'',1,1); +insert into Filters values ('PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0,0,0,0,0,0,'',1,1,0); -- -- Add in some sample control protocol definitions diff --git a/db/zm_update-1.29.1.sql b/db/zm_update-1.29.1.sql index 8fd43c318..e0f4fc77f 100644 --- a/db/zm_update-1.29.1.sql +++ b/db/zm_update-1.29.1.sql @@ -1,7 +1,64 @@ -- -- This updates a 1.29.0 database to 1.29.1 -- +<<<<<<< HEAD -- +======= + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE table_name = 'Storage' + AND table_schema = DATABASE() + ) > 0, +"SELECT 'Storage table exists'", +"CREATE TABLE `Storage` ( + `Id` smallint(5) unsigned NOT NULL auto_increment, + `Path` varchar(64) NOT NULL default '', + `Name` varchar(64) NOT NULL default '', + PRIMARY KEY (`Id`) +)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add StorageId column to Monitors +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'StorageId' + ) > 0, +"SELECT 'Column StorageId exists in Monitors'", +"ALTER TABLE Monitors ADD `StorageId` smallint(5) unsigned AFTER `ServerId`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add StorageId column to Eventss +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Events' + AND table_schema = DATABASE() + AND column_name = 'StorageId' + ) > 0, +"SELECT 'Column StorageId exists in Events'", +"ALTER TABLE Events ADD `StorageId` smallint(5) unsigned AFTER `MonitorId`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; +>>>>>>> storageareas -- Increase the size of the Pid field for FreeBSD ALTER TABLE Logs MODIFY Pid int(10); diff --git a/db/zm_update-1.29.2.sql b/db/zm_update-1.29.2.sql new file mode 100644 index 000000000..231e2ff63 --- /dev/null +++ b/db/zm_update-1.29.2.sql @@ -0,0 +1,73 @@ +-- +-- This updates a 1.29.0 database to 1.30.0 +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'SaveJPEGs' + ) > 0, +"SELECT 'Column SaveJPEGs exists in Monitors'", +"ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'VideoWriter' + ) > 0, +"SELECT 'Column VideoWriter exists in Monitors'", +"ALTER TABLE `Monitors` ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'EncoderParameters' + ) > 0, +"SELECT 'Column EncoderParameters exists in Monitors'", +"ALTER TABLE `Monitors` ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Events' + AND table_schema = DATABASE() + AND column_name = 'DefaultVideo' + ) > 0, +"SELECT 'Column DefaultVideo exists in Events'", +"ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'RecordAudio' + ) > 0, +"SELECT 'Column RecordAudio exists in Monitors'", +"ALTER TABLE `Monitors` ADD `RecordAudio` TINYINT NOT NULL DEFAULT '0' AFTER `EncoderParameters`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.30.0.sql b/db/zm_update-1.30.0.sql new file mode 100644 index 000000000..231e2ff63 --- /dev/null +++ b/db/zm_update-1.30.0.sql @@ -0,0 +1,73 @@ +-- +-- This updates a 1.29.0 database to 1.30.0 +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'SaveJPEGs' + ) > 0, +"SELECT 'Column SaveJPEGs exists in Monitors'", +"ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'VideoWriter' + ) > 0, +"SELECT 'Column VideoWriter exists in Monitors'", +"ALTER TABLE `Monitors` ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'EncoderParameters' + ) > 0, +"SELECT 'Column EncoderParameters exists in Monitors'", +"ALTER TABLE `Monitors` ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Events' + AND table_schema = DATABASE() + AND column_name = 'DefaultVideo' + ) > 0, +"SELECT 'Column DefaultVideo exists in Events'", +"ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'RecordAudio' + ) > 0, +"SELECT 'Column RecordAudio exists in Monitors'", +"ALTER TABLE `Monitors` ADD `RecordAudio` TINYINT NOT NULL DEFAULT '0' AFTER `EncoderParameters`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog index 74cf1d0b8..46a8f3d5e 100644 --- a/distros/ubuntu1204/changelog +++ b/distros/ubuntu1204/changelog @@ -1,3 +1,21 @@ +zoneminder (1.30.2-trusty-2016050201) trusty; urgency=medium + + * add Servers support to API + + -- Isaac Connor Mon, 02 May 2016 10:45:24 -0400 + +zoneminder (1.30.2-trusty-2016042901) trusty; urgency=medium + + * zone edit fixes + + -- Isaac Connor Fri, 29 Apr 2016 10:46:18 -0400 + +zoneminder (1.30.2-trusty-2016042801) trusty; urgency=medium + + * Merge video + + -- Isaac Connor Thu, 28 Apr 2016 12:54:08 -0400 + zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium * include api, switch to cmake build diff --git a/distros/ubuntu1410/changelog b/distros/ubuntu1410/changelog index f155ab9ef..55e3d17b0 100644 --- a/distros/ubuntu1410/changelog +++ b/distros/ubuntu1410/changelog @@ -1,3 +1,39 @@ +zoneminder (1.30.2-trusty-2016033001) trusty; urgency=medium + + * merge master + + -- Isaac Connor Wed, 30 Mar 2016 14:09:48 -0400 + +zoneminder (1.30.2-trusty-2016032901) trusty; urgency=medium + + * filter fixes, merge options rework by Kyle + + -- Isaac Connor Tue, 29 Mar 2016 12:27:57 -0400 + +zoneminder (1.30.2-trusty-2016030702) trusty; urgency=medium + + * + + -- Isaac Connor Mon, 07 Mar 2016 22:14:03 -0500 + +zoneminder (1.30.2-trusty-2016030701) trusty; urgency=medium + + * merge master. with telemetry + + -- Isaac Connor Mon, 07 Mar 2016 21:47:53 -0500 + +zoneminder (1.30.2-trusty-2016022101) trusty; urgency=medium + + * merge zmtrigger fix + + -- Isaac Connor Mon, 22 Feb 2016 09:15:53 -0500 + +zoneminder (1.30.2-trusty-2016021901) trusty; urgency=medium + + * zmtrigger improvements + + -- Isaac Connor Fri, 19 Feb 2016 11:09:57 -0500 + zoneminder (1.30.2-trusty-2016021701) trusty; urgency=medium * printout id, and ip address when failing to connect diff --git a/distros/ubuntu1410/rules b/distros/ubuntu1410/rules index 37fe00538..49d3549f1 100755 --- a/distros/ubuntu1410/rules +++ b/distros/ubuntu1410/rules @@ -53,7 +53,7 @@ override_dh_auto_configure: --with-mariadb=/usr --with-webdir=/usr/share/zoneminder \ --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin \ --with-webuser=www-data --with-webgroup=www-data \ - --enable-crashtrace=no --enable-mmap=yes $(DEBOPT) + --enable-mmap=yes $(DEBOPT) override_dh_clean: # Add here commands to clean up after the build process. diff --git a/distros/ubuntu1504/control b/distros/ubuntu1504/control index c1b7d958f..0b32a37ac 100644 --- a/distros/ubuntu1504/control +++ b/distros/ubuntu1504/control @@ -6,6 +6,7 @@ Uploaders: Vagrant Cascadian Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree ,cmake ,libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libavdevice-ffmpeg-dev + ,libx264-dev, libmp4v2-dev, ,libbz2-dev ,libgcrypt-dev ,libcurl4-gnutls-dev @@ -34,10 +35,8 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,javascript-common ,libav-tools - ,libdate-manip-perl + ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdbd-mysql-perl - ,libmime-lite-perl - ,libmime-tools-perl ,libphp-serialization-perl ,libmodule-load-conditional-perl ,libnet-sftp-foreign-perl @@ -57,12 +56,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libsys-cpu-perl, libsys-meminfo-perl ,mysql-client | virtual-mysql-client ,perl-modules - ,php5-mysql, php5-gd + ,php5-mysql | php-mysql, php5-gd | php-gd ,policykit-1 ,rsyslog | system-log-daemon ,zip Recommends: ${misc:Recommends} - ,libapache2-mod-php5 | php5-fpm + ,libapache2-mod-php5 | libapache2-mod-php7 | php5-fpm ,mysql-server | virtual-mysql-server ,zoneminder-doc (>= ${source:Version}) Suggests: fcgiwrap, logrotate diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst deleted file mode 100644 index 9294ebeb6..000000000 --- a/docs/installationguide/debian.rst +++ /dev/null @@ -1,37 +0,0 @@ -Debian -====== - -A fresh build based on master branch running Debian 7 (wheezy)\: -:: - - root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; - - root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; - root@host:~# cd zoneminder; - root@host:~# ln -s distros/debian; - root@host:~# dpkg-checkbuilddeps; - root@host:~# dpkg-buildpackage; - -One level above you'll now find a deb package matching the architecture of the build host: -:: - - root@host:~# ls -1 ~/zoneminder*; - /root/zoneminder_1.26.4-1_amd64.changes - /root/zoneminder_1.26.4-1_amd64.deb - /root/zoneminder_1.26.4-1.dsc - /root/zoneminder_1.26.4-1.tar.gz - -The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. - -To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed. -:: - - root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; - root@host:~# dpkg-reconfigure zoneminder; - -Alternatively you may also use gdebi to automatically resolve dependencies during installation: -:: - - root@host:~# aptitude install -y gdebi; - root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; - diff --git a/docs/installationguide/images/zm-multiserver.png b/docs/installationguide/images/zm-multiserver.png deleted file mode 100644 index 360b612a8..000000000 Binary files a/docs/installationguide/images/zm-multiserver.png and /dev/null differ diff --git a/docs/installationguide/images/zm-multiserver.xml b/docs/installationguide/images/zm-multiserver.xml deleted file mode 100644 index c829e1a94..000000000 --- a/docs/installationguide/images/zm-multiserver.xml +++ /dev/null @@ -1 +0,0 @@ -7Vvfb+I4F/1rkHYfFpEEQnlsmXZ2pXZUqaPd/fZlZIib+JsQs44pZf76uU6uEydOgELoUgkeELn+fc/JuddO6HnTxetnQZbRAw9o3HMHwWvP+9Rz3SvfhW9l2OSGsTfKDaFgQW5ySsMT+0HROEDrigU0rVSUnMeSLavGOU8SOpcVGxGCr6vVnnlcHXVJQj1iaXiak9i2/sUCGeGyXL+0/05ZGOmRHX+Sl8zI/Hso+CrB8Xqu95x98uIF0X1lC/VuwYeCc+hG/Vq8Tmms/Kh9lHvjrqW0mKSgCU5kRwMHUXgh8QpX+g9P6ANLAirA/rACF//2RMVLdvmFyjUX3+HXV77kMQ83PdePYaibGRT7ofqVd5jKjXZdtniqRnSgeB0xSZ+WZK5K18AVsEVyEWNxKgX/TqfQtwBLAnMB4zOL45op4oL94IkkuiFMUDLA6zpmYQI2yVXPz1AFyeRcFZMzHYQ+U80pkjUzocM+U76gUsA6B1jqISORyB52sC5ZMR5jlchgxFgzmSATw6LnEiD4gRi14OWOLbweSALkW6gF7cYC1qjsBzicoF/nMBBwwXb4ggWBGqYRYQ61n2O+BksE9Sg0MLFR0AguiWRcdeWCA2Fm0AVLQnVdXgHvwKCKVXM9R7ipBgPfn067gdgZDCsYu+MrC+QR3jkmxg7e88dBjJ0YEN+zBO65C7jdgKtRKsAd9xHLHfCimB4Fr2aSARsNINLgJRcy4iFPSHxbWsF7WkGVbwx8aRJcq+BWIgmWO8AXq/6fSrlBGMhKcgVWMcI9z9yt+ml1a8pXIoM6cxzGcElESLHeCAVJLWKr9wWNgQEv1XDa5MmsKayKqFa6wpKzRKZGz4/KYIhyTZUdDMwlKnmPza2dQa31EMO6Hj53A7aqgV2scS/8J+jDjvB/ZfJvZe57Pl7+L7scT/DykQoG01M3ddb4XRnjoYyeIWFGflXia4Sx6g8neOPuQ7ADeKHdYqj+n5DxcjDdX3+xOGNwwJRzkMpp9rG0H0r8qyvnxoOSUJCAAW61IKDNn5iADDoX60SBX3SmM99MsiOyVJNZvEK7ZdRP8tQw7c9WAMEhY9Q0H7Nk72ZpUFhl0zPoyqD1jbYha0GlMfyguxqy4jcHDb8qEOBMO+vTCV7XIWOEzDOo8SS5UBuWCznOkBzDyTuSowjKtnDk+WLAXnTCOI2Vd6HuL183S5Xd/dqUVOYWGNdoaVGsLT9r3oVp6z2Z0fiRpwzBnXEp+QIqxLWCIg+1EtMz5vZyfgJuOTVyTeztZpG9VLabnXALifzfJSswY7OJujbbqOt6o5NnMNrWXQazPyD23tBGyMrwUpi9rNnM+6h2RpJVN1LCaoLY6rad7jB3Vw2M1ba35X12Lj9sUeMdufzOjootoe4o50QXmwJfy8IWWNUZYnGihWac4D4CU5yMkpnusTxwbBKeSTU/9jT1TOFpCmqF8aiUp4nneWBS689Wo+Xd/3eljkuBmN7dnQcf05SHrzJd0ieZW8JePsAl7u0d99Lcp7VTHKXXO4JhQfvd0dBg3aSJdDoEHkM6HWm33YUGDPuBe1Mc/zdlr820afB9K7StZIhIkOm90uyApFERkes4Mp6O+wzUIe2rpyIZkkdhpbPi6oZ5bCfFQ/SviaQ+xznuCHUPPT3bMInuc8cYborwYztwWxjdP2LWRxpO8CzirRGzdcrdR0zd8xsDxGAwnSqpqAeITwTiIkntCGHFA2tTdcywb4hDl13ZUdHpW4opQPc7NHgKXM2U9La+skVrelqkjcdpnb3//3BaN3QgaWtKtTvXuvpIB+8OWqd8gt3B9kf0WrAuWrB/ptq1Bng+hsxCA2wCnyxvdVyk4keWgNGwnjucSgLqIx0sAa1T7l4C7IdDFwk4MwkYTqqvFbyzBOBpzUeWAP/ddjz1kQ7e8bROuXsJsB8CXiTgzCTAr78a+J4SoJ9KNOyIZ3rD+McjVJiSBRXE3nru91Cw6OtCtb2oNl8JJjff5rnTO3mBbTSqbj2KR8AGzTQTO3/crDu+vIB6yncUXa/+jqL99OVkL6DqN5ovL6CeCNyR9Xbx6V5Ahcvy7wR52lH+P8O7/Qk= \ No newline at end of file diff --git a/docs/installationguide/multiserver.rst b/docs/installationguide/multiserver.rst deleted file mode 100644 index e9ebefac2..000000000 --- a/docs/installationguide/multiserver.rst +++ /dev/null @@ -1,55 +0,0 @@ -Multi-Server Install -==================== - -It is possible to run multiple ZoneMinder servers and manage them from a single interface. To achieve this each zoneminder server is connected to a single shared database server and shares file storage for event data. - -.. image:: images/zm-multiserver.png - -Topology Design Notes ---------------------- - -1. Device symbols represent separate logical functions, not necessarily separate hardware. For example, the Database Server and a ZoneMinder Server, can reside on the same physical hardware. - -2. Configure each ZoneMinder Server to use the same, remote Database Server (Green). - -3. The Storage Server (Red) represents shared storage, accessible by all ZoneMinder Servers, mounted under each server’s events folder. - -4. Create at least two networks for best performance. Dedicate a Storage LAN for communication with the Storage and Database Servers. Make use of multipath and jumbo frames if possible. Keep all other traffic off the Storage LAN! Dedicate the second LAN, called the Video LAN in the diagram, for all other traffic. - -New installs ------------- - -1. Follow the normal instructions for your distro for installing ZoneMinder onto all the ZoneMinder servers in the normal fashion. Only a single database will be needed either as standalone, or on one of the ZoneMinder Servers. - -2. On each ZoneMinder server, edit zm.conf. Find the ZM_DB_HOST variable and set it to the name or ip address of your Database Server. Find the ZM_SERVER_HOST and enter a name for this ZoneMinder server. Use a name easily recognizable by you. This name is not used by ZoneMinder for dns or any other form of network conectivity. - -3. Copy the file /usr/share/zoneminder/db/zm_create.sql from one of the ZoneMinder Servers to the machine targeted as the Database Server. - -4. Install mysql/mariadb server onto the Database Server. - -5. It is advised to run "mysql_secure_installation" to help secure the server. - -6. Using the password for the root account set during the previous step, create the ZoneMinder database and configure a database account for ZoneMinder to use: - -:: - - mysql -u root -p < zm_create.sql - mysql -uroot -p -e "grant all on zm.* to 'zmuser'@localhost identified by 'zmpass';" - mysqladmin -u root -p reload - -The database account credentials, zmuser/zmpass, are arbitrary. Set them to anything that suits your environment. -Note that these commands are just an example and might not be secure enough for your environment. - -7. If you have chosen to change the ZoneMinder database account credentials to something other than zmuser/zmpass, you must now update zm.conf on each ZoneMinder Server. Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step. - -Additionally, you must also edit /usr/share/zoneminder/www/api/app/Config/database.php in a similar manner on each ZoneMinder Server. Scroll down and change login and password to the values you created in the previous step. - -8. All ZoneMinders Servers must share a common events folder. This can be done in any manner supported by the underlying operating system. From the Storage Server, share/export a folder to be used for ZoneMinder events. - -9. From each ZoneMinder Server, mount the shared events folder on the Storage Server to the events folder on the local ZoneMinder Server. - -NOTE: The location of this folder varies by distro. This folder is often found under "/var/lib/zoneminder/events" for RedHat based distros and "/var/cache/zoneminder/events" for Debain based distros. This folder is NOT a Symbolic Link! - -10. Open your browser and point it to the web console on any of the ZoneMinder Servers (they will all be the same). Open Options, click the Servers tab,and populate this screen with all of your ZoneMinder Servers. Each server has a field for its name and its hostname. The name is what you used for ZM_SERVER_HOST in step 2. The hostname is the network name or ip address ZoneMinder should use. - -11. When creating a new Monitor, remember to select the server the camera will be assigned to from the Server drop down box. diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 1fe0a96a8..2657b8653 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -19,7 +19,8 @@ bin_SCRIPTS = \ zmcontrol.pl \ zmtrack.pl \ zmcamtool.pl \ - zmsystemctl.pl + zmsystemctl.pl \ + zmtelemetry.pl SUBDIRS = \ . \ @@ -39,6 +40,7 @@ EXTRA_DIST = \ zmtrack.pl.in \ zmcamtool.pl.in \ zmsystemctl.pl.in \ + zmtelemtry.pl.in \ ZoneMinder/Makefile.PL \ ZoneMinder/README \ ZoneMinder/Changes \ diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index c95f65441..a8cebde89 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -155,10 +155,12 @@ MAIN: while( $loop ) { Fatal("ZM_AUDIT_MIN_AGE is not set in config."); } + my %Monitors; my $db_monitors; - my $monitorSelectSql = "select Id from Monitors order by Id"; + my $monitorSelectSql = "select * from Monitors order by Id"; my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() ); + my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age FROM Events WHERE MonitorId = ? ORDER BY Id"; my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) @@ -169,6 +171,8 @@ MAIN: while( $loop ) { or Fatal( "Can't execute: ".$monitorSelectSth->errstr() ); while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) { + $Monitors{$$monitor{Id}} = $monitor; + Debug( "Found database monitor '$monitor->{Id}'" ); my $db_events = $db_monitors->{$monitor->{Id}} = {}; my $res = $eventSelectSth->execute( $monitor->{Id} ) @@ -467,20 +471,30 @@ MAIN: while( $loop ) { # New audit to close any events that were left open for longer than MIN_AGE seconds my $selectUnclosedEventsSql = - "SELECT E.Id, - max(F.TimeStamp) as EndTime, - unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, - max(F.FrameId) as Frames, + #"SELECT E.Id, ANY_VALUE(E.MonitorId), +# + #max(F.TimeStamp) as EndTime, + #unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, + #max(F.FrameId) as Frames, + #count(if(F.Score>0,1,NULL)) as AlarmFrames, + #sum(F.Score) as TotScore, + #max(F.Score) as MaxScore + #FROM Events as E + #INNER JOIN Frames as F on E.Id = F.EventId + #WHERE isnull(E.Frames) or isnull(E.EndTime) + #GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)" + #; + "SELECT *, unix_timestamp(StartTime) AS TimeStamp FROM Events WHERE EndTime IS NULL AND StartTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"; + + my $selectFrameDataSql = "SELECT max(TimeStamp) as EndTime, unix_timestamp(max(TimeStamp)) AS EndTimeStamp, max(FrameId) as Frames, count(if(F.Score>0,1,NULL)) as AlarmFrames, sum(F.Score) as TotScore, - max(F.Score) as MaxScore, - M.EventPrefix as Prefix - FROM Events as E - LEFT JOIN Monitors as M on E.MonitorId = M.Id - INNER JOIN Frames as F on E.Id = F.EventId - WHERE isnull(E.Frames) or isnull(E.EndTime) - GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)" - ; + max(F.Score) as MaxScore + FROM Frames WHERE EventId=?"; + my $selectFrameDataSth = $dbh->prepare_cached($selectFrameDataSql) + or Fatal( "Can't prepare '$selectFrameDataSql': ".$dbh->errstr() ); + + my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql ) or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() ); my $updateUnclosedEventsSql = @@ -505,26 +519,32 @@ MAIN: while( $loop ) { aud_print( "Found open event '$event->{Id}'" ); if ( confirm( 'close', 'closing' ) ) { - $res = $updateUnclosedEventsSth->execute - ( - sprintf("%s%d%s", - $event->{Prefix}, - $event->{Id}, - RECOVER_TAG - ), - $event->{EndTime}, - $event->{Length}, - $event->{Frames}, - $event->{AlarmFrames}, - $event->{TotScore}, - $event->{AlarmFrames} - ? int($event->{TotScore} / $event->{AlarmFrames}) - : 0 - , - $event->{MaxScore}, - RECOVER_TEXT, - $event->{Id} - ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() ); + $res = $selectFrameDataSth->execute( $event->{Id} ); + my $frame = $selectFrameDataSth->fetchrow_hashref(); + if ( $frame ) { + $res = $updateUnclosedEventsSth->execute + ( + sprintf("%s%d%s", + $Monitors{$event->{MonitorId}}->{EventPrefix}, + $event->{Id}, + RECOVER_TAG + ), + $frame->{EndTime}, + $frame->{EndTimeStamp} - $event->{TimeStamp}, + $frame->{Frames}, + $frame->{AlarmFrames}, + $frame->{TotScore}, + $frame->{AlarmFrames} + ? int($frame->{TotScore} / $frame->{AlarmFrames}) + : 0 + , + $frame->{MaxScore}, + RECOVER_TEXT, + $event->{Id} + ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() ); + } else { + Error("SHOULD DELETE"); + } # end if has frame data } } diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 83fb50dc0..7c8244f83 100755 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -246,7 +246,7 @@ sub getFilters } $sth->finish(); if ( ! @filters ) { - Error("No filter found for $sql with values(@sql_values)"); + Warning("No filter found for $sql with values(@sql_values)"); } else { Debug( "Got " . @filters . " filters" ); } diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index 2f180e090..095a9bf6d 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -198,6 +198,7 @@ my %spawned_connections; my %monitors; my $monitor_reload_time = 0; +my $needsReload = 0; $! = undef; my $rin = ''; @@ -218,8 +219,8 @@ while( 1 ) if ( $nfound > 0 ) { Debug( "Got input from $nfound connections\n" ); - foreach my $connection ( @in_select_connections ) - { + foreach my $connection ( @in_select_connections ) { + if ( vec( $rout, $connection->fileno(), 1 ) ) { Debug( "Got input from connection " @@ -311,52 +312,56 @@ while( 1 ) # Check for alarms that might have happened my @out_messages; - foreach my $monitor ( values(%monitors) ) - { - my ( $state, $last_event ) - = zmMemRead( $monitor, - [ "shared_data:state", - "shared_data:last_event" - ] - ); + foreach my $monitor ( values(%monitors) ) { - #print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); - #print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); - if ( $state == STATE_ALARM - || $state == STATE_ALERT - ) # In alarm state - { - if ( !defined($monitor->{LastEvent}) - || ($last_event != $monitor->{LastEvent}) - ) # A new event - { + my $memVerified = 1; + if ( !zmMemRead($monitor, "shared_data:valid") ) { + zmMemInvalidate($monitor); + $memVerified = zmMemVerify($monitor); + } + + if ($memVerified) { + my ( $state, $last_event ) + = zmMemRead( $monitor, + [ "shared_data:state", + "shared_data:last_event" + ] + ); + + #print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); + #print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); + if ( $state == STATE_ALARM + || $state == STATE_ALERT + ) { # In alarm state + if ( !defined($monitor->{LastEvent}) + || ($last_event != $monitor->{LastEvent}) + ) { # A new event + push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); + } else { # The same one as last time, so ignore it + # Do nothing + } + } elsif ( ($state == STATE_IDLE + && $monitor->{LastState} != STATE_IDLE + ) + || ($state == STATE_TAPE + && $monitor->{LastState} != STATE_TAPE + ) + ) { # Out of alarm state + push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); + } + elsif ( defined($monitor->{LastEvent}) + && ($last_event != $monitor->{LastEvent}) + ) { # We've missed a whole event push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); + push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); } - else # The same one as last time, so ignore it - { - # Do nothing - } + $monitor->{LastState} = $state; + $monitor->{LastEvent} = $last_event; + } else { # Our attempt to verify the memory handle failed. We should reload the monitors. + $needsReload = 1; } - elsif ( ($state == STATE_IDLE - && $monitor->{LastState} != STATE_IDLE - ) - || ($state == STATE_TAPE - && $monitor->{LastState} != STATE_TAPE - ) - ) # Out of alarm state - { - push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); - } - elsif ( defined($monitor->{LastEvent}) - && ($last_event != $monitor->{LastEvent}) - ) # We've missed a whole event - { - push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); - push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); - } - $monitor->{LastState} = $state; - $monitor->{LastEvent} = $last_event; } + foreach my $connection ( @out_connections ) { if ( $connection->canWrite() ) @@ -413,7 +418,7 @@ while( 1 ) } # If necessary reload monitors - if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) + if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )) { foreach my $monitor ( values(%monitors) ) { @@ -421,6 +426,7 @@ while( 1 ) zmMemInvalidate( $monitor ); } loadMonitors(); + $needsReload = 0; } } Info( "Trigger daemon exiting\n" ); diff --git a/scripts/zmvideo.pl.in b/scripts/zmvideo.pl.in index 645cacbbd..87c059786 100644 --- a/scripts/zmvideo.pl.in +++ b/scripts/zmvideo.pl.in @@ -258,5 +258,5 @@ if ( $concat_name ) { } #unlink $input_file_list; #print( STDOUT $event->{MonitorId}.'/'.$event->{Id}.'/'.$video_file."\n" ); -#print( STDOUT $video_file."\n" ); +#print( STDOUT $event_path . '/' . $video_file ."\n" ); exit( 0 ); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c943e55e..007ef250f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) -set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_zone.cpp zm_storage.cpp) +set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp) # A fix for cmake recompiling the source files for every target. add_library(zm STATIC ${ZM_BIN_SRC_FILES}) diff --git a/src/zm_camera.cpp b/src/zm_camera.cpp index bbf2edcb6..f8c9fd6af 100644 --- a/src/zm_camera.cpp +++ b/src/zm_camera.cpp @@ -20,30 +20,31 @@ #include "zm.h" #include "zm_camera.h" -Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - id( p_id ), - type( p_type ), - width( p_width), - height( p_height ), - colours( p_colours ), - subpixelorder( p_subpixelorder ), - brightness( p_brightness ), - hue( p_hue ), - colour( p_colour ), - contrast( p_contrast ), - capture( p_capture ) +Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + id( p_id ), + type( p_type ), + width( p_width), + height( p_height ), + colours( p_colours ), + subpixelorder( p_subpixelorder ), + brightness( p_brightness ), + hue( p_hue ), + colour( p_colour ), + contrast( p_contrast ), + capture( p_capture ), + record_audio( p_record_audio ) { - pixels = width * height; - imagesize = pixels * colours; - - Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); - - /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ - if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { - Fatal("Image size is not multiples of 16"); - } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { - Fatal("Image size is not multiples of 12 and 16"); - } + pixels = width * height; + imagesize = pixels * colours; + + Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); + + /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ + if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { + Fatal("Image size is not multiples of 16"); + } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { + Fatal("Image size is not multiples of 12 and 16"); + } } Camera::~Camera() diff --git a/src/zm_camera.h b/src/zm_camera.h index ba8224c09..eb161b9f0 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -32,52 +32,56 @@ class Camera { protected: - typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; + typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; - int id; - SourceType type; - unsigned int width; - unsigned int height; - unsigned int colours; - unsigned int subpixelorder; - unsigned int pixels; - unsigned int imagesize; - int brightness; - int hue; - int colour; - int contrast; - bool capture; + int id; + SourceType type; + unsigned int width; + unsigned int height; + unsigned int colours; + unsigned int subpixelorder; + unsigned int pixels; + unsigned int imagesize; + int brightness; + int hue; + int colour; + int contrast; + bool capture; + bool record_audio; public: - Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - virtual ~Camera(); + Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + virtual ~Camera(); - int getId() const { return( id ); } - SourceType Type() const { return( type ); } - bool IsLocal() const { return( type == LOCAL_SRC ); } - bool IsRemote() const { return( type == REMOTE_SRC ); } - bool IsFile() const { return( type == FILE_SRC ); } - bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } - bool IsLibvlc() const { return( type == LIBVLC_SRC ); } - bool IscURL() const { return( type == CURL_SRC ); } - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } - unsigned int Colours() const { return( colours ); } - unsigned int SubpixelOrder() const { return( subpixelorder ); } - unsigned int Pixels() const { return( pixels ); } - unsigned int ImageSize() const { return( imagesize ); } + int getId() const { return( id ); } + SourceType Type() const { return( type ); } + bool IsLocal() const { return( type == LOCAL_SRC ); } + bool IsRemote() const { return( type == REMOTE_SRC ); } + bool IsFile() const { return( type == FILE_SRC ); } + bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } + bool IsLibvlc() const { return( type == LIBVLC_SRC ); } + bool IscURL() const { return( type == CURL_SRC ); } + unsigned int Width() const { return( width ); } + unsigned int Height() const { return( height ); } + unsigned int Colours() const { return( colours ); } + unsigned int SubpixelOrder() const { return( subpixelorder ); } + unsigned int Pixels() const { return( pixels ); } + unsigned int ImageSize() const { return( imagesize ); } - virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } - virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } - virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } - virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } + virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } + virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } + virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } + virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } - bool CanCapture() const { return( capture ); } - - virtual int PrimeCapture() { return( 0 ); } - virtual int PreCapture()=0; - virtual int Capture( Image &image )=0; - virtual int PostCapture()=0; + bool CanCapture() const { return( capture ); } + + bool SupportsNativeVideo() const { return( (type == FFMPEG_SRC )||(type == REMOTE_SRC)); } + + virtual int PrimeCapture() { return( 0 ); } + virtual int PreCapture()=0; + virtual int Capture( Image &image )=0; + virtual int PostCapture()=0; + virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory)=0; }; #endif // ZM_CAMERA_H diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 26051f113..1a5c65840 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -29,277 +29,279 @@ void zmLoadConfig() { - FILE *cfg; - char line[512]; - if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) - { - Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); - } - while ( fgets( line, sizeof(line), cfg ) != NULL ) - { - char *line_ptr = line; + FILE *cfg; + char line[512]; + if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) + { + Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); + } + while ( fgets( line, sizeof(line), cfg ) != NULL ) + { + char *line_ptr = line; - // Trim off any cr/lf line endings - int chomp_len = strcspn( line_ptr, "\r\n" ); - line_ptr[chomp_len] = '\0'; + // Trim off any cr/lf line endings + int chomp_len = strcspn( line_ptr, "\r\n" ); + line_ptr[chomp_len] = '\0'; - // Remove leading white space - int white_len = strspn( line_ptr, " \t" ); - line_ptr += white_len; + // Remove leading white space + int white_len = strspn( line_ptr, " \t" ); + line_ptr += white_len; - // Check for comment or empty line - if ( *line_ptr == '\0' || *line_ptr == '#' ) - continue; + // Check for comment or empty line + if ( *line_ptr == '\0' || *line_ptr == '#' ) + continue; - // Remove trailing white space - char *temp_ptr = line_ptr+strlen(line_ptr)-1; - while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) - { - *temp_ptr-- = '\0'; - temp_ptr--; - } + // Remove trailing white space + char *temp_ptr = line_ptr+strlen(line_ptr)-1; + while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) + { + *temp_ptr-- = '\0'; + temp_ptr--; + } - // Now look for the '=' in the middle of the line - temp_ptr = strchr( line_ptr, '=' ); - if ( !temp_ptr ) - { - Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); - continue; - } + // Now look for the '=' in the middle of the line + temp_ptr = strchr( line_ptr, '=' ); + if ( !temp_ptr ) + { + Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); + continue; + } - // Assign the name and value parts - char *name_ptr = line_ptr; - char *val_ptr = temp_ptr+1; + // Assign the name and value parts + char *name_ptr = line_ptr; + char *val_ptr = temp_ptr+1; - // Trim trailing space from the name part - do - { - *temp_ptr = '\0'; - temp_ptr--; - } - while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); + // Trim trailing space from the name part + do + { + *temp_ptr = '\0'; + temp_ptr--; + } + while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); - // Remove leading white space from the value part - white_len = strspn( val_ptr, " \t" ); - val_ptr += white_len; + // Remove leading white space from the value part + white_len = strspn( val_ptr, " \t" ); + val_ptr += white_len; - if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) - staticConfig.DB_HOST = std::string(val_ptr); - else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) - staticConfig.DB_NAME = std::string(val_ptr); - else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) - 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_PATH_WEB" ) == 0 ) - staticConfig.PATH_WEB = std::string(val_ptr); - else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 ) - staticConfig.SERVER_NAME = std::string(val_ptr); - else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 ) - staticConfig.SERVER_NAME = std::string(val_ptr); - else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 ) - staticConfig.SERVER_ID = atoi(val_ptr); - else - { - // We ignore this now as there may be more parameters than the - // c/c++ binaries are bothered about - // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); - } - } // end foreach line of the config - fclose( cfg ); - zmDbConnect(); - config.Load(); - config.Assign(); + if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) + staticConfig.DB_HOST = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) + staticConfig.DB_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) + 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_PATH_WEB" ) == 0 ) + staticConfig.PATH_WEB = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 ) + staticConfig.SERVER_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 ) + staticConfig.SERVER_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 ) + staticConfig.SERVER_ID = atoi(val_ptr); + else + { + // We ignore this now as there may be more parameters than the + // c/c++ binaries are bothered about + // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); + } + } // end foreach line of the config + fclose( cfg ); + zmDbConnect(); + config.Load(); + config.Assign(); - // Populate the server config entries - if ( ! staticConfig.SERVER_ID ) { - if ( ! staticConfig.SERVER_NAME.empty() ) { + // Populate the server config entries + if ( ! staticConfig.SERVER_ID ) { + if ( ! staticConfig.SERVER_NAME.empty() ) { - Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); - std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); - if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { - staticConfig.SERVER_ID = atoi(dbrow[0]); - } else { - Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); - } + Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); + std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); + zmDbRow dbrow; + if ( dbrow.fetch( sql.c_str() ) ) { + staticConfig.SERVER_ID = atoi(dbrow[0]); + } else { + Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); + } - } // end if has SERVER_NAME - } else if ( staticConfig.SERVER_NAME.empty() ) { - Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); - std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); - - if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { - staticConfig.SERVER_NAME = std::string(dbrow[0]); - } else { - Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); - } - - } - if ( ! staticConfig.SERVER_ID ) { - Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." ); - } else { - Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID ); - } + } // end if has SERVER_NAME + } else if ( staticConfig.SERVER_NAME.empty() ) { + Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); + std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); + + zmDbRow dbrow; + if ( dbrow.fetch( sql.c_str() ) ) { + staticConfig.SERVER_NAME = std::string(dbrow[0]); + } else { + Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); + } + + } + if ( ! staticConfig.SERVER_ID ) { + Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." ); + } else { + Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID ); + } } StaticConfig staticConfig; ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type ) { - name = new char[strlen(p_name)+1]; - strcpy( name, p_name ); - value = new char[strlen(p_value)+1]; - strcpy( value, p_value ); - type = new char[strlen(p_type)+1]; - strcpy( type, p_type ); + name = new char[strlen(p_name)+1]; + strcpy( name, p_name ); + value = new char[strlen(p_value)+1]; + strcpy( value, p_value ); + type = new char[strlen(p_type)+1]; + strcpy( type, p_type ); - //Info( "Created new config item %s = %s (%s)\n", name, value, type ); + //Info( "Created new config item %s = %s (%s)\n", name, value, type ); - accessed = false; + accessed = false; } ConfigItem::~ConfigItem() { - delete[] name; - delete[] value; - delete[] type; + delete[] name; + delete[] value; + delete[] type; } void ConfigItem::ConvertValue() const { - if ( !strcmp( type, "boolean" ) ) - { - cfg_type = CFG_BOOLEAN; - cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); - } - else if ( !strcmp( type, "integer" ) ) - { - cfg_type = CFG_INTEGER; - cfg_value.integer_value = strtol( value, 0, 10 ); - } - else if ( !strcmp( type, "hexadecimal" ) ) - { - cfg_type = CFG_INTEGER; - cfg_value.integer_value = strtol( value, 0, 16 ); - } - else if ( !strcmp( type, "decimal" ) ) - { - cfg_type = CFG_DECIMAL; - cfg_value.decimal_value = strtod( value, 0 ); - } - else - { - cfg_type = CFG_STRING; - cfg_value.string_value = value; - } - accessed = true; + if ( !strcmp( type, "boolean" ) ) + { + cfg_type = CFG_BOOLEAN; + cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); + } + else if ( !strcmp( type, "integer" ) ) + { + cfg_type = CFG_INTEGER; + cfg_value.integer_value = strtol( value, 0, 10 ); + } + else if ( !strcmp( type, "hexadecimal" ) ) + { + cfg_type = CFG_INTEGER; + cfg_value.integer_value = strtol( value, 0, 16 ); + } + else if ( !strcmp( type, "decimal" ) ) + { + cfg_type = CFG_DECIMAL; + cfg_value.decimal_value = strtod( value, 0 ); + } + else + { + cfg_type = CFG_STRING; + cfg_value.string_value = value; + } + accessed = true; } bool ConfigItem::BooleanValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_BOOLEAN ) - { - Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_BOOLEAN ) + { + Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.boolean_value ); + return( cfg_value.boolean_value ); } int ConfigItem::IntegerValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_INTEGER ) - { - Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_INTEGER ) + { + Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.integer_value ); + return( cfg_value.integer_value ); } double ConfigItem::DecimalValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_DECIMAL ) - { - Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_DECIMAL ) + { + Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.decimal_value ); + return( cfg_value.decimal_value ); } const char *ConfigItem::StringValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_STRING ) - { - Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_STRING ) + { + Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.string_value ); + return( cfg_value.string_value ); } Config::Config() { - n_items = 0; - items = 0; + n_items = 0; + items = 0; } Config::~Config() { - if ( items ) - { - for ( int i = 0; i < n_items; i++ ) - { - delete items[i]; - } - delete[] items; - } + if ( items ) + { + for ( int i = 0; i < n_items; i++ ) + { + delete items[i]; + } + delete[] items; + } } void Config::Load() { - static char sql[ZM_SQL_SML_BUFSIZ]; + static char sql[ZM_SQL_SML_BUFSIZ]; - strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - n_items = mysql_num_rows( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + n_items = mysql_num_rows( result ); - if ( n_items <= ZM_MAX_CFG_ID ) - { - Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); - exit( -1 ); - } + if ( n_items <= ZM_MAX_CFG_ID ) + { + Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); + exit( -1 ); + } - items = new ConfigItem *[n_items]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); - } - mysql_free_result( result ); + items = new ConfigItem *[n_items]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); + } + mysql_free_result( result ); } void Config::Assign() @@ -309,27 +311,27 @@ ZM_CFG_ASSIGN_LIST const ConfigItem &Config::Item( int id ) { - if ( !n_items ) - { - Load(); - Assign(); - } + if ( !n_items ) + { + Load(); + Assign(); + } - if ( id < 0 || id > ZM_MAX_CFG_ID ) - { - Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); - exit( -1 ); - } + if ( id < 0 || id > ZM_MAX_CFG_ID ) + { + Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); + exit( -1 ); + } - ConfigItem *item = items[id]; - - if ( !item ) - { - Error( "Can't find config item %d", id ); - exit( -1 ); - } - - return( *item ); + ConfigItem *item = items[id]; + + if ( !item ) + { + Error( "Can't find config item %d", id ); + exit( -1 ); + } + + return( *item ); } Config config; diff --git a/src/zm_curl_camera.cpp b/src/zm_curl_camera.cpp index f9b6d0712..303b53528 100644 --- a/src/zm_curl_camera.cpp +++ b/src/zm_curl_camera.cpp @@ -30,524 +30,532 @@ const char* content_type_match = "Content-Type:"; size_t content_length_match_len; size_t content_type_match_len; -cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET ) +cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET ) { - if ( capture ) - { - Initialise(); - } + if ( capture ) + { + Initialise(); + } } cURLCamera::~cURLCamera() { - if ( capture ) - { + if ( capture ) + { - Terminate(); - } + Terminate(); + } } void cURLCamera::Initialise() { - content_length_match_len = strlen(content_length_match); - content_type_match_len = strlen(content_type_match); + content_length_match_len = strlen(content_length_match); + content_type_match_len = strlen(content_type_match); - databuffer.expand(CURL_BUFFER_INITIAL_SIZE); + databuffer.expand(CURL_BUFFER_INITIAL_SIZE); - /* cURL initialization */ - cRet = curl_global_init(CURL_GLOBAL_ALL); - if(cRet != CURLE_OK) { - Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); - } + /* cURL initialization */ + cRet = curl_global_init(CURL_GLOBAL_ALL); + if(cRet != CURLE_OK) { + Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); + } - Debug(2,"libcurl version: %s",curl_version()); + Debug(2,"libcurl version: %s",curl_version()); - /* Create the shared data mutex */ - nRet = pthread_mutex_init(&shareddata_mutex, NULL); - if(nRet != 0) { - Fatal("Shared data mutex creation failed: %s",strerror(nRet)); - } - /* Create the data available condition variable */ - nRet = pthread_cond_init(&data_available_cond, NULL); - if(nRet != 0) { - Fatal("Data available condition variable creation failed: %s",strerror(nRet)); - } - /* Create the request complete condition variable */ - nRet = pthread_cond_init(&request_complete_cond, NULL); - if(nRet != 0) { - Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); - } + /* Create the shared data mutex */ + nRet = pthread_mutex_init(&shareddata_mutex, NULL); + if(nRet != 0) { + Fatal("Shared data mutex creation failed: %s",strerror(nRet)); + } + /* Create the data available condition variable */ + nRet = pthread_cond_init(&data_available_cond, NULL); + if(nRet != 0) { + Fatal("Data available condition variable creation failed: %s",strerror(nRet)); + } + /* Create the request complete condition variable */ + nRet = pthread_cond_init(&request_complete_cond, NULL); + if(nRet != 0) { + Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); + } - /* Create the thread */ - nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); - if(nRet != 0) { - Fatal("Thread creation failed: %s",strerror(nRet)); - } + /* Create the thread */ + nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); + if(nRet != 0) { + Fatal("Thread creation failed: %s",strerror(nRet)); + } } void cURLCamera::Terminate() { - /* Signal the thread to terminate */ - bTerminate = true; + /* Signal the thread to terminate */ + bTerminate = true; - /* Wait for thread termination */ - pthread_join(thread, NULL); + /* Wait for thread termination */ + pthread_join(thread, NULL); - /* Destroy condition variables */ - pthread_cond_destroy(&request_complete_cond); - pthread_cond_destroy(&data_available_cond); + /* Destroy condition variables */ + pthread_cond_destroy(&request_complete_cond); + pthread_cond_destroy(&data_available_cond); - /* Destroy mutex */ - pthread_mutex_destroy(&shareddata_mutex); + /* Destroy mutex */ + pthread_mutex_destroy(&shareddata_mutex); - /* cURL cleanup */ - curl_global_cleanup(); + /* cURL cleanup */ + curl_global_cleanup(); } int cURLCamera::PrimeCapture() { - //Info( "Priming capture from %s", mPath.c_str() ); - return 0; + //Info( "Priming capture from %s", mPath.c_str() ); + return 0; } int cURLCamera::PreCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int cURLCamera::Capture( Image &image ) { - bool frameComplete = false; + bool frameComplete = false; - /* MODE_STREAM specific variables */ - bool SubHeadersParsingComplete = false; - unsigned int frame_content_length = 0; - std::string frame_content_type; - bool need_more_data = false; + /* MODE_STREAM specific variables */ + bool SubHeadersParsingComplete = false; + unsigned int frame_content_length = 0; + std::string frame_content_type; + bool need_more_data = false; - /* Grab the mutex to ensure exclusive access to the shared data */ - lock(); + /* Grab the mutex to ensure exclusive access to the shared data */ + lock(); - while (!frameComplete) { + while (!frameComplete) { - /* If the work thread did a reset, reset our local variables */ - if(bReset) { - SubHeadersParsingComplete = false; - frame_content_length = 0; - frame_content_type.clear(); - need_more_data = false; - bReset = false; - } + /* If the work thread did a reset, reset our local variables */ + if(bReset) { + SubHeadersParsingComplete = false; + frame_content_length = 0; + frame_content_type.clear(); + need_more_data = false; + bReset = false; + } - if(mode == MODE_UNSET) { - /* Don't have a mode yet. Sleep while waiting for data */ - nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for available data condition variable: %s",strerror(nRet)); - return -20; - } - } + if(mode == MODE_UNSET) { + /* Don't have a mode yet. Sleep while waiting for data */ + nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for available data condition variable: %s",strerror(nRet)); + return -20; + } + } - if(mode == MODE_STREAM) { + if(mode == MODE_STREAM) { - /* Subheader parsing */ - while(!SubHeadersParsingComplete && !need_more_data) { + /* Subheader parsing */ + while(!SubHeadersParsingComplete && !need_more_data) { - size_t crlf_start, crlf_end, crlf_size; - std::string subheader; + size_t crlf_start, crlf_end, crlf_size; + std::string subheader; - /* Check if the buffer contains something */ - if(databuffer.empty()) { - /* Empty buffer, wait for data */ - need_more_data = true; - break; - } - - /* Find crlf start */ - crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); - if(crlf_start == databuffer.size()) { - /* Not found, wait for more data */ - need_more_data = true; - break; - } + /* Check if the buffer contains something */ + if(databuffer.empty()) { + /* Empty buffer, wait for data */ + need_more_data = true; + break; + } + + /* Find crlf start */ + crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); + if(crlf_start == databuffer.size()) { + /* Not found, wait for more data */ + need_more_data = true; + break; + } - /* See if we have enough data for determining crlf length */ - if(databuffer.size() < crlf_start+5) { - /* Need more data */ - need_more_data = true; - break; - } + /* See if we have enough data for determining crlf length */ + if(databuffer.size() < crlf_start+5) { + /* Need more data */ + need_more_data = true; + break; + } - /* Find crlf end and calculate crlf size */ - crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); - crlf_size = (crlf_start + crlf_end) - crlf_start; + /* Find crlf end and calculate crlf size */ + crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); + crlf_size = (crlf_start + crlf_end) - crlf_start; - /* Is this the end of a previous stream? (This is just before the boundary) */ - if(crlf_start == 0) { - databuffer.consume(crlf_size); - continue; - } + /* Is this the end of a previous stream? (This is just before the boundary) */ + if(crlf_start == 0) { + databuffer.consume(crlf_size); + continue; + } - /* Check for invalid CRLF size */ - if(crlf_size > 4) { - Error("Invalid CRLF length"); - } + /* Check for invalid CRLF size */ + if(crlf_size > 4) { + Error("Invalid CRLF length"); + } - /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ - if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { - /* This is the last header */ - SubHeadersParsingComplete = true; - } + /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ + if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { + /* This is the last header */ + SubHeadersParsingComplete = true; + } - /* Copy the subheader, excluding the crlf */ - subheader.assign(databuffer, crlf_start); + /* Copy the subheader, excluding the crlf */ + subheader.assign(databuffer, crlf_start); - /* Advance the buffer past this one */ - databuffer.consume(crlf_start+crlf_size); + /* Advance the buffer past this one */ + databuffer.consume(crlf_start+crlf_size); - Debug(7,"Got subheader: %s",subheader.c_str()); + Debug(7,"Got subheader: %s",subheader.c_str()); - /* Find where the data in this header starts */ - size_t subheader_data_start = subheader.rfind(' '); - if(subheader_data_start == std::string::npos) { - subheader_data_start = subheader.find(':'); - } + /* Find where the data in this header starts */ + size_t subheader_data_start = subheader.rfind(' '); + if(subheader_data_start == std::string::npos) { + subheader_data_start = subheader.find(':'); + } - /* Extract the data into a string */ - std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); + /* Extract the data into a string */ + std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); - Debug(8,"Got subheader data: %s",subheader_data.c_str()); + Debug(8,"Got subheader data: %s",subheader_data.c_str()); - /* Check the header */ - if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { - /* Found the content-length header */ - frame_content_length = atoi(subheader_data.c_str()); - Debug(6,"Got content-length subheader: %d",frame_content_length); - } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { - /* Found the content-type header */ - frame_content_type = subheader_data; - Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); - } + /* Check the header */ + if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { + /* Found the content-length header */ + frame_content_length = atoi(subheader_data.c_str()); + Debug(6,"Got content-length subheader: %d",frame_content_length); + } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { + /* Found the content-type header */ + frame_content_type = subheader_data; + Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); + } - } + } - /* Attempt to extract the frame */ - if(!need_more_data) { - if(!SubHeadersParsingComplete) { - /* We haven't parsed all headers yet */ - need_more_data = true; - } else if(frame_content_length <= 0) { - /* Invalid frame */ - Error("Invalid frame: invalid content length"); - } else if(frame_content_type != "image/jpeg") { - /* Unsupported frame type */ - Error("Unsupported frame: %s",frame_content_type.c_str()); - } else if(frame_content_length > databuffer.size()) { - /* Incomplete frame, wait for more data */ - need_more_data = true; - } else { - /* All good. decode the image */ - image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); - frameComplete = true; - } - } + /* Attempt to extract the frame */ + if(!need_more_data) { + if(!SubHeadersParsingComplete) { + /* We haven't parsed all headers yet */ + need_more_data = true; + } else if(frame_content_length <= 0) { + /* Invalid frame */ + Error("Invalid frame: invalid content length"); + } else if(frame_content_type != "image/jpeg") { + /* Unsupported frame type */ + Error("Unsupported frame: %s",frame_content_type.c_str()); + } else if(frame_content_length > databuffer.size()) { + /* Incomplete frame, wait for more data */ + need_more_data = true; + } else { + /* All good. decode the image */ + image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); + frameComplete = true; + } + } - /* Attempt to get more data */ - if(need_more_data) { - nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for available data condition variable: %s",strerror(nRet)); - return -18; - } - need_more_data = false; - } + /* Attempt to get more data */ + if(need_more_data) { + nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for available data condition variable: %s",strerror(nRet)); + return -18; + } + need_more_data = false; + } - } else if(mode == MODE_SINGLE) { - /* Check if we have anything */ - if (!single_offsets.empty()) { - if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { - /* Extract frame */ - image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); - single_offsets.pop_front(); - frameComplete = true; - } else { - /* This shouldn't happen */ - Error("Internal error. Attempting recovery"); - databuffer.consume(single_offsets.front()); - single_offsets.pop_front(); - } - } else { - /* Don't have a frame yet, wait for the request complete condition variable */ - nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); - return -19; - } - } - } else { - /* Failed to match content-type */ - Fatal("Unable to match Content-Type. Check URL, username and password"); - } /* mode */ + } else if(mode == MODE_SINGLE) { + /* Check if we have anything */ + if (!single_offsets.empty()) { + if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { + /* Extract frame */ + image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); + single_offsets.pop_front(); + frameComplete = true; + } else { + /* This shouldn't happen */ + Error("Internal error. Attempting recovery"); + databuffer.consume(single_offsets.front()); + single_offsets.pop_front(); + } + } else { + /* Don't have a frame yet, wait for the request complete condition variable */ + nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); + return -19; + } + } + } else { + /* Failed to match content-type */ + Fatal("Unable to match Content-Type. Check URL, username and password"); + } /* mode */ - } /* frameComplete loop */ + } /* frameComplete loop */ - /* Release the mutex */ - unlock(); + /* Release the mutex */ + unlock(); - if(!frameComplete) - return -1; + if(!frameComplete) + return -1; - return 0; + return 0; } int cURLCamera::PostCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } +int cURLCamera::CaptureAndRecord( Image &image, bool recording, char* event_directory ) +{ + Error("Capture and Record not implemented for the cURL camera type"); + // Nothing to do here + return( 0 ); +} + + size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) { - lock(); + lock(); - /* Append the data we just received to our buffer */ - databuffer.append((const char*)buffer, size*nmemb); + /* Append the data we just received to our buffer */ + databuffer.append((const char*)buffer, size*nmemb); - /* Signal data available */ - nRet = pthread_cond_signal(&data_available_cond); - if(nRet != 0) { - Error("Failed signaling data available condition variable: %s",strerror(nRet)); - return -16; - } + /* Signal data available */ + nRet = pthread_cond_signal(&data_available_cond); + if(nRet != 0) { + Error("Failed signaling data available condition variable: %s",strerror(nRet)); + return -16; + } - unlock(); + unlock(); - /* Return bytes processed */ - return size*nmemb; + /* Return bytes processed */ + return size*nmemb; } size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) { - std::string header; - header.assign((const char*)buffer, size*nmemb); - - Debug(4,"Got header: %s",header.c_str()); + std::string header; + header.assign((const char*)buffer, size*nmemb); + + Debug(4,"Got header: %s",header.c_str()); - /* Check Content-Type header */ - if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) { - size_t pos = header.find(';'); - if(pos != std::string::npos) { - header.erase(pos, std::string::npos); - } + /* Check Content-Type header */ + if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) { + size_t pos = header.find(';'); + if(pos != std::string::npos) { + header.erase(pos, std::string::npos); + } - pos = header.rfind(' '); - if(pos == std::string::npos) { - pos = header.find(':'); - } + pos = header.rfind(' '); + if(pos == std::string::npos) { + pos = header.find(':'); + } - std::string content_type = header.substr(pos+1, std::string::npos); - Debug(6,"Content-Type is: %s",content_type.c_str()); + std::string content_type = header.substr(pos+1, std::string::npos); + Debug(6,"Content-Type is: %s",content_type.c_str()); - lock(); + lock(); - const char* multipart_match = "multipart/x-mixed-replace"; - const char* image_jpeg_match = "image/jpeg"; - if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) { - Debug(7,"Content type matched as multipart/x-mixed-replace"); - mode = MODE_STREAM; - } else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) { - Debug(7,"Content type matched as image/jpeg"); - mode = MODE_SINGLE; - } + const char* multipart_match = "multipart/x-mixed-replace"; + const char* image_jpeg_match = "image/jpeg"; + if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) { + Debug(7,"Content type matched as multipart/x-mixed-replace"); + mode = MODE_STREAM; + } else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) { + Debug(7,"Content type matched as image/jpeg"); + mode = MODE_SINGLE; + } - unlock(); - } - - /* Return bytes processed */ - return size*nmemb; + unlock(); + } + + /* Return bytes processed */ + return size*nmemb; } void* cURLCamera::thread_func() { - long tRet; - double dSize; + long tRet; + double dSize; - c = curl_easy_init(); - if(c == NULL) { - Fatal("Failed getting easy handle from libcurl"); - } + c = curl_easy_init(); + if(c == NULL) { + Fatal("Failed getting easy handle from libcurl"); + } - /* Set URL */ - cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); - - /* Header callback */ - cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); + /* Set URL */ + cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); + + /* Header callback */ + cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); - /* Data callback */ - cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); + /* Data callback */ + cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); - /* Progress callback */ - cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); - if(cRet != CURLE_OK) - Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); + /* Progress callback */ + cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); + if(cRet != CURLE_OK) + Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); - /* Set username and password */ - if(!mUser.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); - if(cRet != CURLE_OK) - Error("Failed setting username: %s", curl_easy_strerror(cRet)); - } - if(!mPass.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); - if(cRet != CURLE_OK) - Error("Failed setting password: %s", curl_easy_strerror(cRet)); - } + /* Set username and password */ + if(!mUser.empty()) { + cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); + if(cRet != CURLE_OK) + Error("Failed setting username: %s", curl_easy_strerror(cRet)); + } + if(!mPass.empty()) { + cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); + if(cRet != CURLE_OK) + Error("Failed setting password: %s", curl_easy_strerror(cRet)); + } - /* Authenication preference */ - cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - if(cRet != CURLE_OK) - Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); + /* Authenication preference */ + cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + if(cRet != CURLE_OK) + Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); - /* Work loop */ - for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) { - tRet = 0; - while(!bTerminate) { - /* Do the work */ - cRet = curl_easy_perform(c); + /* Work loop */ + for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) { + tRet = 0; + while(!bTerminate) { + /* Do the work */ + cRet = curl_easy_perform(c); - if(mode == MODE_SINGLE) { - if(cRet != CURLE_OK) { - break; - } - /* Attempt to get the size of the file */ - cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); - if(cRet != CURLE_OK) { - break; - } - /* We need to lock for the offsets array and the condition variable */ - lock(); - /* Push the size into our offsets array */ - if(dSize > 0) { - single_offsets.push_back(dSize); - } else { - Fatal("Unable to get the size of the image"); - } - /* Signal the request complete condition variable */ - tRet = pthread_cond_signal(&request_complete_cond); - if(tRet != 0) { - Error("Failed signaling request completed condition variable: %s",strerror(tRet)); - } - /* Unlock */ - unlock(); + if(mode == MODE_SINGLE) { + if(cRet != CURLE_OK) { + break; + } + /* Attempt to get the size of the file */ + cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); + if(cRet != CURLE_OK) { + break; + } + /* We need to lock for the offsets array and the condition variable */ + lock(); + /* Push the size into our offsets array */ + if(dSize > 0) { + single_offsets.push_back(dSize); + } else { + Fatal("Unable to get the size of the image"); + } + /* Signal the request complete condition variable */ + tRet = pthread_cond_signal(&request_complete_cond); + if(tRet != 0) { + Error("Failed signaling request completed condition variable: %s",strerror(tRet)); + } + /* Unlock */ + unlock(); - } else if (mode == MODE_STREAM) { - break; - } - } + } else if (mode == MODE_STREAM) { + break; + } + } - /* Return value checking */ - if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) { - /* Aborted */ - break; - } else if (cRet != CURLE_OK) { - /* Some error */ - Error("cURL Request failed: %s",curl_easy_strerror(cRet)); - if(attempt < CURL_MAXRETRY) { - Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); - /* Do a reset */ - lock(); - databuffer.clear(); - single_offsets.clear(); - mode = MODE_UNSET; - bReset = true; - unlock(); - } - tRet = -50; - } - } - - /* Cleanup */ - curl_easy_cleanup(c); - c = NULL; - - return (void*)tRet; + /* Return value checking */ + if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) { + /* Aborted */ + break; + } else if (cRet != CURLE_OK) { + /* Some error */ + Error("cURL Request failed: %s",curl_easy_strerror(cRet)); + if(attempt < CURL_MAXRETRY) { + Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); + /* Do a reset */ + lock(); + databuffer.clear(); + single_offsets.clear(); + mode = MODE_UNSET; + bReset = true; + unlock(); + } + tRet = -50; + } + } + + /* Cleanup */ + curl_easy_cleanup(c); + c = NULL; + + return (void*)tRet; } int cURLCamera::lock() { - int nRet; + int nRet; - /* Lock shared data */ - nRet = pthread_mutex_lock(&shareddata_mutex); - if(nRet != 0) { - Error("Failed locking shared data mutex: %s",strerror(nRet)); - } - return nRet; + /* Lock shared data */ + nRet = pthread_mutex_lock(&shareddata_mutex); + if(nRet != 0) { + Error("Failed locking shared data mutex: %s",strerror(nRet)); + } + return nRet; } int cURLCamera::unlock() { - int nRet; + int nRet; - /* Unlock shared data */ - nRet = pthread_mutex_unlock(&shareddata_mutex); - if(nRet != 0) { - Error("Failed unlocking shared data mutex: %s",strerror(nRet)); - } - return nRet; + /* Unlock shared data */ + nRet = pthread_mutex_unlock(&shareddata_mutex); + if(nRet != 0) { + Error("Failed unlocking shared data mutex: %s",strerror(nRet)); + } + return nRet; } int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) { - /* Signal the curl thread to terminate */ - if(bTerminate) - return -10; - - return 0; + /* Signal the curl thread to terminate */ + if(bTerminate) + return -10; + + return 0; } /* These functions call the functions in the class for the correct object */ size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) { - return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata); + return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata); } size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) { - return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata); + return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata); } int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) { - return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow); + return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow); } void* thread_func_dispatcher(void* object) { - return ((cURLCamera*)object)->thread_func(); + return ((cURLCamera*)object)->thread_func(); } diff --git a/src/zm_curl_camera.h b/src/zm_curl_camera.h index f7c3540fe..dcf39f94a 100644 --- a/src/zm_curl_camera.h +++ b/src/zm_curl_camera.h @@ -42,55 +42,57 @@ class cURLCamera : public Camera { protected: - typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t; + typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t; - std::string mPath; - std::string mUser; - std::string mPass; + std::string mPath; + std::string mUser; + std::string mPass; - /* cURL object(s) */ - CURL* c; + /* cURL object(s) */ + CURL* c; - /* Shared data */ - volatile bool bTerminate; - volatile bool bReset; - volatile mode_t mode; - Buffer databuffer; - std::deque single_offsets; + /* Shared data */ + volatile bool bTerminate; + volatile bool bReset; + volatile mode_t mode; + Buffer databuffer; + std::deque single_offsets; - /* pthread objects */ - pthread_t thread; - pthread_mutex_t shareddata_mutex; - pthread_cond_t data_available_cond; - pthread_cond_t request_complete_cond; + /* pthread objects */ + pthread_t thread; + pthread_mutex_t shareddata_mutex; + pthread_cond_t data_available_cond; + pthread_cond_t request_complete_cond; public: - cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~cURLCamera(); + cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~cURLCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Username() const { return( mUser ); } - const std::string &Password() const { return( mPass ); } + const std::string &Path() const { return( mPath ); } + const std::string &Username() const { return( mUser ); } + const std::string &Password() const { return( mPass ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, bool recording, char* event_directory); - size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); - size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata); - int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow); - int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data); - void* thread_func(); - int lock(); - int unlock(); + + size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); + size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata); + int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow); + int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data); + void* thread_func(); + int lock(); + int unlock(); private: - int nRet; - CURLcode cRet; + int nRet; + CURLcode cRet; }; diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 8f2527fc2..f157c7ab7 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -95,19 +95,38 @@ MYSQL_RES * zmDbFetch( const char * query ) { return result; } // end MYSQL_RES * zmDbFetch( const char * query ); -MYSQL_ROW zmDbFetchOne( const char *query ) { - MYSQL_RES *result = zmDbFetch( query ); - int n_rows = mysql_num_rows( result ); +zmDbRow *zmDbFetchOne( const char *query ) { + zmDbRow *row = new zmDbRow(); + if ( row->fetch( query ) ) { + return row; + } + delete row; + return NULL; +} + +MYSQL_RES *zmDbRow::fetch( const char *query ) { + result_set = zmDbFetch( query ); + if ( ! result_set ) return result_set; + + int n_rows = mysql_num_rows( result_set ); if ( n_rows != 1 ) { Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query ); - return NULL; + mysql_free_result( result_set ); + result_set = NULL; + return result_set; } - MYSQL_ROW dbrow = mysql_fetch_row( result ); - mysql_free_result( result ); - if ( ! dbrow ) { + row = mysql_fetch_row( result_set ); + if ( ! row ) { + mysql_free_result( result_set ); + result_set = NULL; Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) ); - return NULL; + } else { + Debug(3, "Succes"); } - return dbrow; + return result_set; +} +zmDbRow::~zmDbRow() { + if ( result_set ) + mysql_free_result( result_set ); } diff --git a/src/zm_db.h b/src/zm_db.h index 6ec1b5e4e..50ae2974f 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -22,6 +22,21 @@ #include +class zmDbRow { + private: + MYSQL_RES *result_set; + MYSQL_ROW row; + public: + zmDbRow() { result_set = NULL; row = NULL; }; + MYSQL_RES *fetch( const char *query ); + zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); + ~zmDbRow(); + + char *operator[](unsigned int index) const { + return row[index]; + } +}; + #ifdef __cplusplus extern "C" { #endif @@ -33,7 +48,7 @@ void zmDbConnect(); void zmDbClose(); MYSQL_RES * zmDbFetch( const char *query ); -MYSQL_ROW zmDbFetchOne( const char *query ); +zmDbRow *zmDbFetchOne( const char *query ); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/zm_event.cpp b/src/zm_event.cpp index b6e76ae97..29fea1e27 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -54,1548 +54,1581 @@ bool Event::initialised = false; char Event::capture_file_format[PATH_MAX]; char Event::analyse_file_format[PATH_MAX]; char Event::general_file_format[PATH_MAX]; +char Event::video_file_format[PATH_MAX]; int Event::pre_alarm_count = 0; Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } }; -Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ) : - monitor( p_monitor ), - start_time( p_start_time ), - cause( p_cause ), - noteSetMap( p_noteSetMap ) +Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent ) : + monitor( p_monitor ), + start_time( p_start_time ), + cause( p_cause ), + noteSetMap( p_noteSetMap ), + videoEvent( p_videoEvent ), + videowriter( NULL ) { -<<<<<<< HEAD - if ( !initialised ) - Initialise(); + if ( !initialised ) + Initialise(); - std::string notes; - createNotes( notes ); + std::string notes; + createNotes( notes ); - bool untimedEvent = false; - if ( !start_time.tv_sec ) - { - untimedEvent = true; - gettimeofday( &start_time, 0 ); - } + bool untimedEvent = false; + if ( !start_time.tv_sec ) + { + untimedEvent = true; + gettimeofday( &start_time, 0 ); + } Storage * storage = monitor->getStorage(); - static char sql[ZM_SQL_MED_BUFSIZ]; + static char sql[ZM_SQL_MED_BUFSIZ]; - struct tm *stime = localtime( &start_time.tv_sec ); - snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), storage->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); - if ( mysql_query( &dbconn, sql ) ) -======= - if ( !initialised ) - Initialise(); + struct tm *stime = localtime( &start_time.tv_sec ); + snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, Videoed ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d )", monitor->Id(), storage->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str(), videoEvent ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql ); + exit( mysql_errno( &dbconn ) ); + } + id = mysql_insert_id( &dbconn ); + if ( untimedEvent ) + { + Warning( "Event %d has zero time, setting to current", id ); + } + end_time.tv_sec = 0; + frames = 0; + alarm_frames = 0; + tot_score = 0; + max_score = 0; - std::string notes; - createNotes( notes ); + if ( config.use_deep_storage ) + { + char *path_ptr = path; + path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id() ); - bool untimedEvent = false; - if ( !start_time.tv_sec ) - { - untimedEvent = true; - gettimeofday( &start_time, 0 ); - } + int dt_parts[6]; + dt_parts[0] = stime->tm_year-100; + dt_parts[1] = stime->tm_mon+1; + dt_parts[2] = stime->tm_mday; + dt_parts[3] = stime->tm_hour; + dt_parts[4] = stime->tm_min; + dt_parts[5] = stime->tm_sec; - static char sql[ZM_SQL_MED_BUFSIZ]; + char date_path[PATH_MAX] = ""; + char time_path[PATH_MAX] = ""; + char *time_path_ptr = time_path; + for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) + { + path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); - struct tm *stime = localtime( &start_time.tv_sec ); - snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - id = mysql_insert_id( &dbconn ); - if ( untimedEvent ) - { - Warning( "Event %d has zero time, setting to current", id ); - } - end_time.tv_sec = 0; - frames = 0; - alarm_frames = 0; - tot_score = 0; - max_score = 0; + struct stat statbuf; + errno = 0; + if ( stat( path, &statbuf ) ) { + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Fatal( "Can't mkdir %s: %s", path, strerror(errno)); + } + } else { + Warning( "Error stat'ing %s, may be fatal. error is %s", path, strerror(errno)); + } + } + if ( i == 2 ) + strncpy( date_path, path, sizeof(date_path) ); + else if ( i >= 3 ) + time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); + } + char id_file[PATH_MAX]; + // Create event id symlink + snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); + if ( symlink( time_path, id_file ) < 0 ) + Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); + else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + else + { + snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); - if ( config.use_deep_storage ) - { - char *path_ptr = path; - path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't mkdir %s: %s", path, strerror(errno)); + } + } + char id_file[PATH_MAX]; + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); + else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + last_db_frame = 0; - int dt_parts[6]; - dt_parts[0] = stime->tm_year-100; - dt_parts[1] = stime->tm_mon+1; - dt_parts[2] = stime->tm_mday; - dt_parts[3] = stime->tm_hour; - dt_parts[4] = stime->tm_min; - dt_parts[5] = stime->tm_sec; + video_name[0] = 0; - char date_path[PATH_MAX] = ""; - char time_path[PATH_MAX] = ""; - char *time_path_ptr = time_path; - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) ->>>>>>> master - { - path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); + /* Save as video */ -<<<<<<< HEAD - if ( config.use_deep_storage ) - { - char *path_ptr = path; - path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id() ); + if ( monitor->GetOptVideoWriter() != 0 ) { + int nRet; + snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" ); + snprintf( video_file, sizeof(video_file), video_file_format, path, video_name ); + snprintf( timecodes_name, sizeof(timecodes_name), "%d-%s", id, "video.timecodes" ); + snprintf( timecodes_file, sizeof(timecodes_file), video_file_format, path, timecodes_name ); - int dt_parts[6]; - dt_parts[0] = stime->tm_year-100; - dt_parts[1] = stime->tm_mon+1; - dt_parts[2] = stime->tm_mday; - dt_parts[3] = stime->tm_hour; - dt_parts[4] = stime->tm_min; - dt_parts[5] = stime->tm_sec; - char date_path[PATH_MAX] = ""; - char time_path[PATH_MAX] = ""; - char *time_path_ptr = time_path; - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) -======= - struct stat statbuf; - errno = 0; - if ( stat( path, &statbuf ) ) { - if ( errno == ENOENT || errno == ENOTDIR ) ->>>>>>> master - { - if ( mkdir( path, 0755 ) ) - { - Fatal( "Can't mkdir %s: %s", path, strerror(errno)); - } - } else { - Warning( "Error stat'ing %s, may be fatal. error is %s", path, strerror(errno)); - } - } - if ( i == 2 ) - strncpy( date_path, path, sizeof(date_path) ); - else if ( i >= 3 ) - time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); - } - char id_file[PATH_MAX]; - // Create event id symlink - snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); - if ( symlink( time_path, id_file ) < 0 ) - Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); - } - else - { - snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { -<<<<<<< HEAD - snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - } - } - char id_file[PATH_MAX]; - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); -======= - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - } ->>>>>>> master - } - char id_file[PATH_MAX]; - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); - } - last_db_frame = 0; + /* X264 MP4 video writer */ + if(monitor->GetOptVideoWriter() == 1) { +#if ZM_HAVE_VIDEOWRITER_X264MP4 + videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams()); +#else + videowriter = NULL; + Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)"); +#endif + } + + if(videowriter != NULL) { + /* Open the video stream */ + nRet = videowriter->Open(); + if(nRet != 0) { + Error("Failed opening video stream"); + delete videowriter; + videowriter = NULL; + } + + /* Create timecodes file */ + timecodes_fd = fopen(timecodes_file, "wb"); + if(timecodes_fd == NULL) { + Error("Failed creating timecodes file"); + } + } + } else { + /* No video object */ + videowriter = NULL; + } + } Event::~Event() { - if ( frames > last_db_frame ) - { - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + if ( frames > last_db_frame ) + { + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - Debug( 1, "Adding closing frame %d to DB", frames ); - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } + Debug( 1, "Adding closing frame %d to DB", frames ); + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + } - static char sql[ZM_SQL_MED_BUFSIZ]; + /* Close the video file */ + if ( videowriter != NULL ) { + int nRet; - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + nRet = videowriter->Close(); + if(nRet != 0) { + Error("Failed closing video stream"); + } + delete videowriter; + videowriter = NULL; - snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + /* Close the timecodes file */ + fclose(timecodes_fd); + timecodes_fd = NULL; + } + + static char sql[ZM_SQL_MED_BUFSIZ]; + + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + + snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Event::createNotes( std::string ¬es ) { - notes.clear(); - for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) - { - notes += mapIter->first; - notes += ": "; - const StringSet &stringSet = mapIter->second; - for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) - { - if ( setIter != stringSet.begin() ) - notes += ", "; - notes += *setIter; - } - } + notes.clear(); + for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) + { + notes += mapIter->first; + notes += ": "; + const StringSet &stringSet = mapIter->second; + for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) + { + if ( setIter != stringSet.begin() ) + notes += ", "; + notes += *setIter; + } + } } int Event::sd = -1; bool Event::OpenFrameSocket( int monitor_id ) { - if ( sd > 0 ) - { - close( sd ); - } + if ( sd > 0 ) + { + close( sd ); + } - sd = socket( AF_UNIX, SOCK_STREAM, 0); - if ( sd < 0 ) - { - Error( "Can't create socket: %s", strerror(errno) ); - return( false ); - } + sd = socket( AF_UNIX, SOCK_STREAM, 0); + if ( sd < 0 ) + { + Error( "Can't create socket: %s", strerror(errno) ); + return( false ); + } - int socket_buffer_size = config.frame_socket_size; - if ( socket_buffer_size > 0 ) - { - if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) - { - Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - } + int socket_buffer_size = config.frame_socket_size; + if ( socket_buffer_size > 0 ) + { + if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) + { + Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } + } - int flags; - if ( (flags = fcntl( sd, F_GETFL )) < 0 ) - { - Error( "Can't get socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - flags |= O_NONBLOCK; - if ( fcntl( sd, F_SETFL, flags ) < 0 ) - { - Error( "Can't set socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } + int flags; + if ( (flags = fcntl( sd, F_GETFL )) < 0 ) + { + Error( "Can't get socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } + flags |= O_NONBLOCK; + if ( fcntl( sd, F_SETFL, flags ) < 0 ) + { + Error( "Can't set socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - char sock_path[PATH_MAX] = ""; - snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); + char sock_path[PATH_MAX] = ""; + snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); - struct sockaddr_un addr; + struct sockaddr_un addr; - strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); - addr.sun_family = AF_UNIX; + strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); + addr.sun_family = AF_UNIX; - if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) - { - Warning( "Can't connect to frame server: %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } + if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) + { + Warning( "Can't connect to frame server: %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - Debug( 1, "Opened connection to frame server" ); - return( true ); + Debug( 1, "Opened connection to frame server" ); + return( true ); } bool Event::ValidateFrameSocket( int monitor_id ) { - if ( sd < 0 ) - { - return( OpenFrameSocket( monitor_id ) ); - } - return( true ); + if ( sd < 0 ) + { + return( OpenFrameSocket( monitor_id ) ); + } + return( true ); } bool Event::SendFrameImage( const Image *image, bool alarm_frame ) { - if ( !ValidateFrameSocket( monitor->Id() ) ) - { - return( false ); - } + if ( !ValidateFrameSocket( monitor->Id() ) ) + { + return( false ); + } - static int jpg_buffer_size = 0; - static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; + static int jpg_buffer_size = 0; + static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; - image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); + image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); - static FrameHeader frame_header; + static FrameHeader frame_header; - frame_header.event_id = id; - if ( config.use_deep_storage ) - frame_header.event_time = start_time.tv_sec; - frame_header.frame_id = frames; - frame_header.alarm_frame = alarm_frame; - frame_header.image_length = jpg_buffer_size; + frame_header.event_id = id; + if ( config.use_deep_storage ) + frame_header.event_time = start_time.tv_sec; + frame_header.frame_id = frames; + frame_header.alarm_frame = alarm_frame; + frame_header.image_length = jpg_buffer_size; - struct iovec iovecs[2]; - iovecs[0].iov_base = &frame_header; - iovecs[0].iov_len = sizeof(frame_header); - iovecs[1].iov_base = jpg_buffer; - iovecs[1].iov_len = jpg_buffer_size; + struct iovec iovecs[2]; + iovecs[0].iov_base = &frame_header; + iovecs[0].iov_len = sizeof(frame_header); + iovecs[1].iov_base = jpg_buffer; + iovecs[1].iov_len = jpg_buffer_size; - ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; - ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); - if ( writev_result != writev_size ) - { - if ( writev_result < 0 ) - { - if ( errno == EAGAIN ) - { - Warning( "Blocking write detected" ); - } - else - { - Error( "Can't write frame: %s", strerror(errno) ); - close( sd ); - sd = -1; - } - } - else - { - Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); - close( sd ); - sd = -1; - } - return( false ); - } - Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); + ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; + ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); + if ( writev_result != writev_size ) + { + if ( writev_result < 0 ) + { + if ( errno == EAGAIN ) + { + Warning( "Blocking write detected" ); + } + else + { + Error( "Can't write frame: %s", strerror(errno) ); + close( sd ); + sd = -1; + } + } + else + { + Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); + close( sd ); + sd = -1; + } + return( false ); + } + Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); - return( true ); + return( true ); } bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { - Image* ImgToWrite; - Image* ts_image = NULL; + Image* ImgToWrite; + Image* ts_image = NULL; - if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. - { - ts_image = new Image(*image); - monitor->TimestampImage( ts_image, ×tamp ); - ImgToWrite=ts_image; - } - else - ImgToWrite=image; + if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. + { + ts_image = new Image(*image); + monitor->TimestampImage( ts_image, ×tamp ); + ImgToWrite=ts_image; + } + else + ImgToWrite=image; - if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) - { - int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default - ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write - } - if(ts_image) delete(ts_image); // clean up if used. - return( true ); + if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) + { + int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default + ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write + } + if(ts_image) delete(ts_image); // clean up if used. + return( true ); +} + +bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow ) +{ + const Image* frameimg = image; + Image ts_image; + + /* Checking for invalid parameters */ + if ( videow == NULL ) { + Error("NULL Video object"); + return false; + } + + /* If the image does not contain a timestamp, add the timestamp */ + if (!config.timestamp_on_capture) { + ts_image = *image; + monitor->TimestampImage( &ts_image, ×tamp ); + frameimg = &ts_image; + } + + /* Calculate delta time */ + struct DeltaTimeval delta_time3; + DELTA_TIMEVAL( delta_time3, timestamp, start_time, DT_PREC_3 ); + unsigned int timeMS = (delta_time3.sec * delta_time3.prec) + delta_time3.fsec; + + /* Encode and write the frame */ + if(videowriter->Encode(frameimg, timeMS) != 0) { + Error("Failed encoding video frame"); + } + + /* Add the frame to the timecodes file */ + fprintf(timecodes_fd, "%u\n", timeMS); + + return( true ); } void Event::updateNotes( const StringSetMap &newNoteSetMap ) { - bool update = false; + bool update = false; - //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); - if ( newNoteSetMap.size() > 0 ) - { - if ( noteSetMap.size() == 0 ) - { - noteSetMap = newNoteSetMap; - update = true; - } - else - { - for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) - { - const std::string &newNoteGroup = newNoteSetMapIter->first; - const StringSet &newNoteSet = newNoteSetMapIter->second; - //Info( "Got %d new strings", newNoteSet.size() ); - if ( newNoteSet.size() > 0 ) - { - StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); - if ( noteSetMapIter == noteSetMap.end() ) - { - //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); - update = true; - } - else - { - StringSet ¬eSet = noteSetMapIter->second; - //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) - { - const std::string &newNote = *newNoteSetIter; - StringSet::iterator noteSetIter = noteSet.find( newNote ); - if ( noteSetIter == noteSet.end() ) - { - noteSet.insert( newNote ); - update = true; - } - } - } - } - } - } - } + //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); + if ( newNoteSetMap.size() > 0 ) + { + if ( noteSetMap.size() == 0 ) + { + noteSetMap = newNoteSetMap; + update = true; + } + else + { + for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) + { + const std::string &newNoteGroup = newNoteSetMapIter->first; + const StringSet &newNoteSet = newNoteSetMapIter->second; + //Info( "Got %d new strings", newNoteSet.size() ); + if ( newNoteSet.size() > 0 ) + { + StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); + if ( noteSetMapIter == noteSetMap.end() ) + { + //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); + update = true; + } + else + { + StringSet ¬eSet = noteSetMapIter->second; + //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) + { + const std::string &newNote = *newNoteSetIter; + StringSet::iterator noteSetIter = noteSet.find( newNote ); + if ( noteSetIter == noteSet.end() ) + { + noteSet.insert( newNote ); + update = true; + } + } + } + } + } + } + } - if ( update ) - { - std::string notes; - createNotes( notes ); + if ( update ) + { + std::string notes; + createNotes( notes ); - Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); - static char sql[ZM_SQL_MED_BUFSIZ]; + Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); + static char sql[ZM_SQL_MED_BUFSIZ]; #if USE_PREPARED_SQL - static MYSQL_STMT *stmt = 0; + static MYSQL_STMT *stmt = 0; - char notesStr[ZM_SQL_MED_BUFSIZ] = ""; - unsigned long notesLen = 0; + char notesStr[ZM_SQL_MED_BUFSIZ] = ""; + unsigned long notesLen = 0; - if ( !stmt ) - { - const char *sql = "update Events set Notes = ? where Id = ?"; + if ( !stmt ) + { + const char *sql = "update Events set Notes = ? where Id = ?"; - stmt = mysql_stmt_init( &dbconn ); - if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) - { - Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } + stmt = mysql_stmt_init( &dbconn ); + if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) + { + Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } - /* Get the parameter count from the statement */ - if ( mysql_stmt_param_count( stmt ) != 2 ) - { - Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); - } + /* Get the parameter count from the statement */ + if ( mysql_stmt_param_count( stmt ) != 2 ) + { + Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); + } - MYSQL_BIND bind[2]; - memset(bind, 0, sizeof(bind)); + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); - /* STRING PARAM */ - bind[0].buffer_type = MYSQL_TYPE_STRING; - bind[0].buffer = (char *)notesStr; - bind[0].buffer_length = sizeof(notesStr); - bind[0].is_null = 0; - bind[0].length = ¬esLen; + /* STRING PARAM */ + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = (char *)notesStr; + bind[0].buffer_length = sizeof(notesStr); + bind[0].is_null = 0; + bind[0].length = ¬esLen; - bind[1].buffer_type= MYSQL_TYPE_LONG; - bind[1].buffer= (char *)&id; - bind[1].is_null= 0; - bind[1].length= 0; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= (char *)&id; + bind[1].is_null= 0; + bind[1].length= 0; - /* Bind the buffers */ - if ( mysql_stmt_bind_param( stmt, bind ) ) - { - Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } - } + /* Bind the buffers */ + if ( mysql_stmt_bind_param( stmt, bind ) ) + { + Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } + } - strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); - notesLen = notes.length(); + strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); + notesLen = notes.length(); - if ( mysql_stmt_execute( stmt ) ) - { - Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } + if ( mysql_stmt_execute( stmt ) ) + { + Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } #else - static char escapedNotes[ZM_SQL_MED_BUFSIZ]; + static char escapedNotes[ZM_SQL_MED_BUFSIZ]; - mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); + mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); - snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - } + snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + } #endif - } + } } void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { - for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { - AddFramesInternal(n_frames, i, images, timestamps); - } + for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { + AddFramesInternal(n_frames, i, images, timestamps); + } } void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) { - static char sql[ZM_SQL_LGE_BUFSIZ]; - strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); - int frameCount = 0; - for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) - { - if ( !timestamps[i]->tv_sec ) - { - Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); - continue; - } + static char sql[ZM_SQL_LGE_BUFSIZ]; + strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); + int frameCount = 0; + for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) + { + if ( !timestamps[i]->tv_sec ) + { + Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); + continue; + } - frames++; + frames++; - static char event_file[PATH_MAX]; - snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); + static char event_file[PATH_MAX]; + snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); + if ( monitor->GetOptSaveJPEGs() & 4) { + //If this is the first frame, we should add a thumbnail to the event directory + if(frames == 10){ + char snapshot_file[PATH_MAX]; + snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path ); + WriteFrameImage( images[i], *(timestamps[i]), snapshot_file ); + } + } + if ( monitor->GetOptSaveJPEGs() & 1) { + Debug( 1, "Writing pre-capture frame %d", frames ); + WriteFrameImage( images[i], *(timestamps[i]), event_file ); + } + if ( videowriter != NULL ) { + WriteFrameVideo( images[i], *(timestamps[i]), videowriter ); + } - Debug( 1, "Writing pre-capture frame %d", frames ); - WriteFrameImage( images[i], *(timestamps[i]), event_file ); + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); + int sql_len = strlen(sql); + snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - int sql_len = strlen(sql); - snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); + frameCount++; + } - frameCount++; - } - - if ( frameCount ) - { - Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); - *(sql+strlen(sql)-2) = '\0'; - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; - } - else - { - Debug( 1, "No valid pre-capture frames to add" ); - } + if ( frameCount ) + { + Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); + *(sql+strlen(sql)-2) = '\0'; + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + last_db_frame = frames; + } + else + { + Debug( 1, "No valid pre-capture frames to add" ); + } } void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) { - if ( !timestamp.tv_sec ) - { - Debug( 1, "Not adding new frame, zero timestamp" ); - return; - } + if ( !timestamp.tv_sec ) + { + Debug( 1, "Not adding new frame, zero timestamp" ); + return; + } - frames++; + frames++; - static char event_file[PATH_MAX]; - snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); + static char event_file[PATH_MAX]; + snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); - Debug( 1, "Writing capture frame %d", frames ); - WriteFrameImage( image, timestamp, event_file ); + if ( monitor->GetOptSaveJPEGs() & 4) { + //If this is the first frame, we should add a thumbnail to the event directory + if(frames == 10){ + char snapshot_file[PATH_MAX]; + snprintf( snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path ); + WriteFrameImage( image, timestamp, snapshot_file ); + } + } + if( monitor->GetOptSaveJPEGs() & 1) { + Debug( 1, "Writing capture frame %d", frames ); + WriteFrameImage( image, timestamp, event_file ); + } + if ( videowriter != NULL ) { + WriteFrameVideo( image, timestamp, videowriter ); + } - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); - const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); - if ( score < 0 ) - score = 0; + const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); + if ( score < 0 ) + score = 0; - bool db_frame = (strcmp(frame_type,"Bulk") != 0) || ((frames%config.bulk_frame_interval)==0) || !frames; - if ( db_frame ) - { + bool db_frame = (strcmp(frame_type,"Bulk") != 0) || ((frames%config.bulk_frame_interval)==0) || !frames; + if ( db_frame ) + { - Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, frame_type ); - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; + Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, frame_type ); + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + last_db_frame = frames; - // We are writing a Bulk frame - if ( !strcmp( frame_type,"Bulk") ) - { - snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } - } + // We are writing a Bulk frame + if ( !strcmp( frame_type,"Bulk") ) + { + snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + } + } - end_time = timestamp; + end_time = timestamp; - // We are writing an Alarm frame - if ( !strcmp( frame_type,"Alarm") ) - { - alarm_frames++; + // We are writing an Alarm frame + if ( !strcmp( frame_type,"Alarm") ) + { + alarm_frames++; - tot_score += score; - if ( score > (int)max_score ) - max_score = score; + tot_score += score; + if ( score > (int)max_score ) + max_score = score; - if ( alarm_image ) - { - snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); + if ( alarm_image ) + { + snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); - Debug( 1, "Writing analysis frame %d", frames ); - WriteFrameImage( alarm_image, timestamp, event_file, true ); - } - } - - /* This makes viewing the diagnostic images impossible because it keeps deleting them - if ( config.record_diag_images ) - { - char diag_glob[PATH_MAX] = ""; + Debug( 1, "Writing analysis frame %d", frames ); + if ( monitor->GetOptSaveJPEGs() & 2) { + WriteFrameImage( alarm_image, timestamp, event_file, true ); + } + } + } - snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); - glob_t pglob; - int glob_status = glob( diag_glob, 0, 0, &pglob ); - if ( glob_status != 0 ) - { - if ( glob_status < 0 ) - { - Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); - } - else - { - Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); - } - } - else - { - char new_diag_path[PATH_MAX] = ""; - for ( int i = 0; i < pglob.gl_pathc; i++ ) - { - char *diag_path = pglob.gl_pathv[i]; + /* This makes viewing the diagnostic images impossible because it keeps deleting them + if ( config.record_diag_images ) + { + char diag_glob[PATH_MAX] = ""; - char *diag_file = strstr( diag_path, "diag-" ); + snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); + glob_t pglob; + int glob_status = glob( diag_glob, 0, 0, &pglob ); + if ( glob_status != 0 ) + { + if ( glob_status < 0 ) + { + Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); + } + else + { + Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); + } + } + else + { + char new_diag_path[PATH_MAX] = ""; + for ( int i = 0; i < pglob.gl_pathc; i++ ) + { + char *diag_path = pglob.gl_pathv[i]; - if ( diag_file ) - { - snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); + char *diag_file = strstr( diag_path, "diag-" ); - if ( rename( diag_path, new_diag_path ) < 0 ) - { - Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); - } - } - } - } - globfree( &pglob ); - } - */ + if ( diag_file ) + { + snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); + + if ( rename( diag_path, new_diag_path ) < 0 ) + { + Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); + } + } + } + } + globfree( &pglob ); + } + */ } bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { - static char sql[ZM_SQL_SML_BUFSIZ]; + static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - int init_event_id = atoi( dbrow[0] ); + int init_event_id = atoi( dbrow[0] ); - mysql_free_result( result ); + mysql_free_result( result ); - loadEventData( init_event_id ); + loadEventData( init_event_id ); - if ( event_time ) - { - curr_stream_time = event_time; - curr_frame_id = 1; - if ( event_time >= event_data->start_time ) - { - for (unsigned int i = 0; i < event_data->frame_count; i++ ) - { - //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); - if ( event_data->frames[i].timestamp >= event_time ) - { - curr_frame_id = i+1; - Debug( 3, "Set cst:%.2f", curr_stream_time ); - Debug( 3, "Set cfid:%d", curr_frame_id ); - break; - } - } - Debug( 3, "Skipping %ld frames", event_data->frame_count ); - } - } - return( true ); + if ( event_time ) + { + curr_stream_time = event_time; + curr_frame_id = 1; + if ( event_time >= event_data->start_time ) + { + for (unsigned int i = 0; i < event_data->frame_count; i++ ) + { + //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); + if ( event_data->frames[i].timestamp >= event_time ) + { + curr_frame_id = i+1; + Debug( 3, "Set cst:%.2f", curr_stream_time ); + Debug( 3, "Set cfid:%d", curr_frame_id ); + break; + } + } + Debug( 3, "Skipping %ld frames", event_data->frame_count ); + } + } + return( true ); } bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) { - loadEventData( init_event_id ); + loadEventData( init_event_id ); - if ( init_frame_id ) - { - curr_stream_time = event_data->frames[init_frame_id-1].timestamp; - curr_frame_id = init_frame_id; - } - else - { - curr_stream_time = event_data->start_time; - } + if ( init_frame_id ) + { + curr_stream_time = event_data->frames[init_frame_id-1].timestamp; + curr_frame_id = init_frame_id; + } + else + { + curr_stream_time = event_data->start_time; + } - return( true ); + return( true ); } bool EventStream::loadEventData( int event_id ) { - static char sql[ZM_SQL_MED_BUFSIZ]; + static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); + snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.StorageId, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration,E.DefaultVideo from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( !mysql_num_rows( result ) ) - { - Fatal( "Unable to load event %d, not found in DB", event_id ); - } + if ( !mysql_num_rows( result ) ) + { + Fatal( "Unable to load event %d, not found in DB", event_id ); + } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row( result ); - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - delete event_data; - event_data = new EventData; - event_data->event_id = event_id; - event_data->monitor_id = atoi( dbrow[0] ); - event_data->start_time = atoi(dbrow[3]); - if ( config.use_deep_storage ) - { - struct tm *event_time = localtime( &event_data->start_time ); - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - } - else - { - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); - } - event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); - event_data->duration = atof(dbrow[4]); - - updateFrameRate( (double)event_data->frame_count/event_data->duration ); - - mysql_free_result( result ); - - snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - event_data->n_frames = mysql_num_rows( result ); - - event_data->frames = new FrameData[event_data->frame_count]; - int id, last_id = 0; - time_t timestamp, last_timestamp = event_data->start_time; - double delta, last_delta = 0.0; - while ( ( dbrow = mysql_fetch_row( result ) ) ) - { - id = atoi(dbrow[0]); - timestamp = atoi(dbrow[1]); - delta = atof(dbrow[2]); - int id_diff = id - last_id; - double frame_delta = (delta-last_delta)/id_diff; - if ( id_diff > 1 ) - { - for ( int i = last_id+1; i < id; i++ ) - { - event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); - event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); - event_data->frames[i-1].delta = frame_delta; - event_data->frames[i-1].in_db = false; - } - } - event_data->frames[id-1].timestamp = timestamp; - event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); - event_data->frames[id-1].delta = id>1?frame_delta:0.0; - event_data->frames[id-1].in_db = true; - last_id = id; - last_delta = delta; - last_timestamp = timestamp; - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - //for ( int i = 0; i < 250; i++ ) - //{ - //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); - //} - - mysql_free_result( result ); - - if ( forceEventChange || mode == MODE_ALL_GAPLESS ) - { - if ( replay_rate > 0 ) - curr_stream_time = event_data->frames[0].timestamp; - else - curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; - } - Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); - - return( true ); -} - -void EventStream::processCommand( const CmdMsg *msg ) -{ -<<<<<<< HEAD - static char sql[ZM_SQL_MED_BUFSIZ]; - - snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.StorageId, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); -======= - Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) - // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) - { - case CMD_PAUSE : - { - Debug( 1, "Got PAUSE command" ); ->>>>>>> master - - // Set paused flag - paused = true; - replay_rate = ZM_RATE_BASE; - last_frame_sent = TV_2_FLOAT( now ); - break; - } - case CMD_PLAY : - { - Debug( 1, "Got PLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - - // If we are in single event mode and at the last frame, replay the current event - if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) - curr_frame_id = 1; - - replay_rate = ZM_RATE_BASE; - break; - } - case CMD_VARPLAY : - { - Debug( 1, "Got VARPLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - break; - } - case CMD_STOP : - { - Debug( 1, "Got STOP command" ); - - // Clear paused flag - paused = false; - break; - } - case CMD_FASTFWD : - { - Debug( 1, "Got FAST FWD command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case 2 * ZM_RATE_BASE : - replay_rate = 5 * ZM_RATE_BASE; - break; - case 5 * ZM_RATE_BASE : - replay_rate = 10 * ZM_RATE_BASE; - break; - case 10 * ZM_RATE_BASE : - replay_rate = 25 * ZM_RATE_BASE; - break; - case 25 * ZM_RATE_BASE : - case 50 * ZM_RATE_BASE : - replay_rate = 50 * ZM_RATE_BASE; - break; - default : - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_SLOWFWD : - { - Debug( 1, "Got SLOW FWD command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = 1; - break; - } -<<<<<<< HEAD - - delete event_data; - event_data = new EventData; - event_data->event_id = event_id; - event_data->monitor_id = atoi( dbrow[0] ); + delete event_data; + event_data = new EventData; + event_data->event_id = event_id; + event_data->monitor_id = atoi( dbrow[0] ); event_data->storage_id = dbrow[2] ? atoi( dbrow[2] ) : 0; - event_data->start_time = atoi(dbrow[4]); + event_data->start_time = atoi(dbrow[4]); Storage * storage = new Storage( event_data->storage_id ); const char *storage_path = storage->Path(); - if ( config.use_deep_storage ) - { - struct tm *event_time = localtime( &event_data->start_time ); - if ( storage_path[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); -======= - case CMD_SLOWREV : - { - Debug( 1, "Got SLOW REV command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = -1; - break; ->>>>>>> master - } - case CMD_FASTREV : - { -<<<<<<< HEAD - if ( storage_path[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", storage_path, event_data->monitor_id, event_data->event_id ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id ); - } - event_data->frame_count = dbrow[3] == NULL ? 0 : atoi(dbrow[3]); - event_data->duration = atof(dbrow[5]); + if ( config.use_deep_storage ) + { + struct tm *event_time = localtime( &event_data->start_time ); + if ( storage_path[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + } + else + { + if ( storage_path[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", storage_path, event_data->monitor_id, event_data->event_id ); + else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id ); + } + event_data->frame_count = dbrow[3] == NULL ? 0 : atoi(dbrow[3]); + event_data->duration = atof(dbrow[5]); + strncpy( event_data->video_file, dbrow[6], sizeof( event_data->video_file )-1 ); - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate( (double)event_data->frame_count/event_data->duration ); - mysql_free_result( result ); + mysql_free_result( result ); - snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); - if ( mysql_query( &dbconn, sql ) ) -======= - Debug( 1, "Got FAST REV command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case -2 * ZM_RATE_BASE : - replay_rate = -5 * ZM_RATE_BASE; - break; - case -5 * ZM_RATE_BASE : - replay_rate = -10 * ZM_RATE_BASE; - break; - case -10 * ZM_RATE_BASE : - replay_rate = -25 * ZM_RATE_BASE; - break; - case -25 * ZM_RATE_BASE : - case -50 * ZM_RATE_BASE : - replay_rate = -50 * ZM_RATE_BASE; - break; - default : - replay_rate = -2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_ZOOMIN : ->>>>>>> master - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); - switch ( zoom ) - { - case 100: - zoom = 150; - break; - case 150: - zoom = 200; - break; - case 200: - zoom = 300; - break; - case 300: - zoom = 400; - break; - case 400: - default : - zoom = 500; - break; - } - break; - } - case CMD_ZOOMOUT : - { - Debug( 1, "Got ZOOM OUT command" ); - switch ( zoom ) - { - case 500: - zoom = 400; - break; - case 400: - zoom = 300; - break; - case 300: - zoom = 200; - break; - case 200: - zoom = 150; - break; - case 150: - default : - zoom = 100; - break; - } - break; - } - case CMD_PAN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); - break; - } - case CMD_SCALE : - { - scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); - break; - } - case CMD_PREV : - { - Debug( 1, "Got PREV command" ); - if ( replay_rate >= 0 ) - curr_frame_id = 0; - else - curr_frame_id = event_data->frame_count+1; - paused = false; - forceEventChange = true; - break; - } - case CMD_NEXT : - { - Debug( 1, "Got NEXT command" ); - if ( replay_rate >= 0 ) - curr_frame_id = event_data->frame_count+1; - else - curr_frame_id = 0; - paused = false; - forceEventChange = true; - break; - } - case CMD_SEEK : - { - int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); - Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); - break; - } - case CMD_QUERY : - { - Debug( 1, "Got QUERY command, sending STATUS" ); - break; - } - case CMD_QUIT : - { - Info ("User initiated exit - CMD_QUIT"); - break; - } - default : - { - // Do nothing, for now - } - } - struct { - int event; - int progress; - int rate; - int zoom; - bool paused; - } status_data; + snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - status_data.event = event_data->event_id; - status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; - status_data.rate = replay_rate; - status_data.zoom = zoom; - status_data.paused = paused; - Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", - status_data.event, - status_data.paused, - status_data.progress, - status_data.rate, - status_data.zoom - ); + result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - DataMsg status_msg; - status_msg.msg_type = MSG_DATA_EVENT; - memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); - if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) - { - //if ( errno != EAGAIN ) - { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); - exit( -1 ); - } - } - // quit after sending a status, if this was a quit request - if ((MsgCommand)msg->msg_data[0]==CMD_QUIT) - exit(0); + event_data->n_frames = mysql_num_rows( result ); - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + event_data->frames = new FrameData[event_data->frame_count]; + int id, last_id = 0; + time_t timestamp, last_timestamp = event_data->start_time; + double delta, last_delta = 0.0; + while ( ( dbrow = mysql_fetch_row( result ) ) ) + { + id = atoi(dbrow[0]); + timestamp = atoi(dbrow[1]); + delta = atof(dbrow[2]); + int id_diff = id - last_id; + double frame_delta = (delta-last_delta)/id_diff; + if ( id_diff > 1 ) + { + for ( int i = last_id+1; i < id; i++ ) + { + event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); + event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); + event_data->frames[i-1].delta = frame_delta; + event_data->frames[i-1].in_db = false; + } + } + event_data->frames[id-1].timestamp = timestamp; + event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); + event_data->frames[id-1].delta = id>1?frame_delta:0.0; + event_data->frames[id-1].in_db = true; + last_id = id; + last_delta = delta; + last_timestamp = timestamp; + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + //for ( int i = 0; i < 250; i++ ) + //{ + //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); + //} + + mysql_free_result( result ); + + if ( forceEventChange || mode == MODE_ALL_GAPLESS ) + { + if ( replay_rate > 0 ) + curr_stream_time = event_data->frames[0].timestamp; + else + curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; + } + Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); + + return( true ); +} + +void EventStream::processCommand( const CmdMsg *msg ) +{ + Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) + // Check for incoming command + switch( (MsgCommand)msg->msg_data[0] ) + { + case CMD_PAUSE : + { + Debug( 1, "Got PAUSE command" ); + + // Set paused flag + paused = true; + replay_rate = ZM_RATE_BASE; + last_frame_sent = TV_2_FLOAT( now ); + break; + } + case CMD_PLAY : + { + Debug( 1, "Got PLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + + // If we are in single event mode and at the last frame, replay the current event + if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) + curr_frame_id = 1; + + replay_rate = ZM_RATE_BASE; + break; + } + case CMD_VARPLAY : + { + Debug( 1, "Got VARPLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + break; + } + case CMD_STOP : + { + Debug( 1, "Got STOP command" ); + + // Clear paused flag + paused = false; + break; + } + case CMD_FASTFWD : + { + Debug( 1, "Got FAST FWD command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + // Set play rate + switch ( replay_rate ) + { + case 2 * ZM_RATE_BASE : + replay_rate = 5 * ZM_RATE_BASE; + break; + case 5 * ZM_RATE_BASE : + replay_rate = 10 * ZM_RATE_BASE; + break; + case 10 * ZM_RATE_BASE : + replay_rate = 25 * ZM_RATE_BASE; + break; + case 25 * ZM_RATE_BASE : + case 50 * ZM_RATE_BASE : + replay_rate = 50 * ZM_RATE_BASE; + break; + default : + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_SLOWFWD : + { + Debug( 1, "Got SLOW FWD command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = 1; + break; + } + case CMD_SLOWREV : + { + Debug( 1, "Got SLOW REV command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = -1; + break; + } + case CMD_FASTREV : + { + Debug( 1, "Got FAST REV command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + // Set play rate + switch ( replay_rate ) + { + case -2 * ZM_RATE_BASE : + replay_rate = -5 * ZM_RATE_BASE; + break; + case -5 * ZM_RATE_BASE : + replay_rate = -10 * ZM_RATE_BASE; + break; + case -10 * ZM_RATE_BASE : + replay_rate = -25 * ZM_RATE_BASE; + break; + case -25 * ZM_RATE_BASE : + case -50 * ZM_RATE_BASE : + replay_rate = -50 * ZM_RATE_BASE; + break; + default : + replay_rate = -2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_ZOOMIN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + switch ( zoom ) + { + case 100: + zoom = 150; + break; + case 150: + zoom = 200; + break; + case 200: + zoom = 300; + break; + case 300: + zoom = 400; + break; + case 400: + default : + zoom = 500; + break; + } + break; + } + case CMD_ZOOMOUT : + { + Debug( 1, "Got ZOOM OUT command" ); + switch ( zoom ) + { + case 500: + zoom = 400; + break; + case 400: + zoom = 300; + break; + case 300: + zoom = 200; + break; + case 200: + zoom = 150; + break; + case 150: + default : + zoom = 100; + break; + } + break; + } + case CMD_PAN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got PAN command, to %d,%d", x, y ); + break; + } + case CMD_SCALE : + { + scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + Debug( 1, "Got SCALE command, to %d", scale ); + break; + } + case CMD_PREV : + { + Debug( 1, "Got PREV command" ); + if ( replay_rate >= 0 ) + curr_frame_id = 0; + else + curr_frame_id = event_data->frame_count+1; + paused = false; + forceEventChange = true; + break; + } + case CMD_NEXT : + { + Debug( 1, "Got NEXT command" ); + if ( replay_rate >= 0 ) + curr_frame_id = event_data->frame_count+1; + else + curr_frame_id = 0; + paused = false; + forceEventChange = true; + break; + } + case CMD_SEEK : + { + int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); + Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); + break; + } + case CMD_QUERY : + { + Debug( 1, "Got QUERY command, sending STATUS" ); + break; + } + case CMD_QUIT : + { + Info ("User initiated exit - CMD_QUIT"); + break; + } + default : + { + // Do nothing, for now + } + } + struct { + int event; + int progress; + int rate; + int zoom; + bool paused; + } status_data; + + status_data.event = event_data->event_id; + status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; + status_data.rate = replay_rate; + status_data.zoom = zoom; + status_data.paused = paused; + Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", + status_data.event, + status_data.paused, + status_data.progress, + status_data.rate, + status_data.zoom + ); + + DataMsg status_msg; + status_msg.msg_type = MSG_DATA_EVENT; + memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); + if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) + { + //if ( errno != EAGAIN ) + { + Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + exit( -1 ); + } + } + // quit after sending a status, if this was a quit request + if ((MsgCommand)msg->msg_data[0]==CMD_QUIT) + exit(0); + + updateFrameRate( (double)event_data->frame_count/event_data->duration ); } void EventStream::checkEventLoaded() { - bool reload_event = false; - static char sql[ZM_SQL_SML_BUFSIZ]; + bool reload_event = false; + static char sql[ZM_SQL_SML_BUFSIZ]; - if ( curr_frame_id <= 0 ) - { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } - else if ( (unsigned int)curr_frame_id > event_data->frame_count ) - { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } + if ( curr_frame_id <= 0 ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } + else if ( (unsigned int)curr_frame_id > event_data->frame_count ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } - if ( reload_event ) - { - if ( forceEventChange || mode != MODE_SINGLE ) - { - //Info( "SQL:%s", sql ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( reload_event ) + { + if ( forceEventChange || mode != MODE_SINGLE ) + { + //Info( "SQL:%s", sql ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( dbrow ) - { - int event_id = atoi(dbrow[0]); - Debug( 1, "Loading new event %d", event_id ); + if ( dbrow ) + { + int event_id = atoi(dbrow[0]); + Debug( 1, "Loading new event %d", event_id ); - loadEventData( event_id ); + loadEventData( event_id ); - Debug( 2, "Current frame id = %d", curr_frame_id ); - if ( replay_rate < 0 ) - curr_frame_id = event_data->frame_count; - else - curr_frame_id = 1; - Debug( 2, "New frame id = %d", curr_frame_id ); - } - else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } - mysql_free_result( result ); - forceEventChange = false; - } - else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } - } + Debug( 2, "Current frame id = %d", curr_frame_id ); + if ( replay_rate < 0 ) + curr_frame_id = event_data->frame_count; + else + curr_frame_id = 1; + Debug( 2, "New frame id = %d", curr_frame_id ); + } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + mysql_free_result( result ); + forceEventChange = false; + } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + } +} + +Image * EventStream::getImage( ) { + Event::Initialise(); + static char filepath[PATH_MAX]; + + Debug( 2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id ); + snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); + Debug( 2, "EventStream::getImage path(%s) ", filepath, curr_frame_id ); + Image *image = new Image( filepath ); + return image; } bool EventStream::sendFrame( int delta_us ) { - Debug( 2, "Sending frame %d", curr_frame_id ); + Debug( 2, "Sending frame %d", curr_frame_id ); - static char filepath[PATH_MAX]; - static struct stat filestat; - FILE *fdj = NULL; - - snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); + static char filepath[PATH_MAX]; + static struct stat filestat; + FILE *fdj = NULL; + + snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) - { - Image image( filepath ); + if ( type == STREAM_MPEG ) + { + Image image( filepath ); - Image *send_image = prepareImage( &image ); + Image *send_image = prepareImage( &image ); - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); - } - else + if ( !vid_stream ) + { + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); + } + /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + } + else #endif // HAVE_LIBAVCODEC - { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; - int img_buffer_size = 0; - uint8_t *img_buffer = temp_img_buffer; + int img_buffer_size = 0; + uint8_t *img_buffer = temp_img_buffer; - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf( stdout, "--ZoneMinderFrame\r\n" ); - if ( type != STREAM_JPEG ) - send_raw = false; + if ( type != STREAM_JPEG ) + send_raw = false; - if ( send_raw ) - { - fdj = fopen( filepath, "rb" ); - if ( !fdj ) - { - Error( "Can't open %s: %s", filepath, strerror(errno) ); - return( false ); - } -#if HAVE_SENDFILE - if( fstat(fileno(fdj),&filestat) < 0 ) { - Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); - return( false ); - } + if ( send_raw ) + { + fdj = fopen( filepath, "rb" ); + if ( !fdj ) + { + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return( false ); + } +#if HAVE_SENDFILE + if( fstat(fileno(fdj),&filestat) < 0 ) { + Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); + return( false ); + } #else - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); #endif - } - else - { - Image image( filepath ); + } + else + { + Image image( filepath ); - Image *send_image = prepareImage( &image ); + Image *send_image = prepareImage( &image ); - switch( type ) - { - case STREAM_JPEG : - send_image->EncodeJpeg( img_buffer, &img_buffer_size ); - break; - case STREAM_ZIP : + switch( type ) + { + case STREAM_JPEG : + send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + break; + case STREAM_ZIP : #if HAVE_ZLIB_H - unsigned long zip_buffer_size; - send_image->Zip( img_buffer, &zip_buffer_size ); - img_buffer_size = zip_buffer_size; - break; + unsigned long zip_buffer_size; + send_image->Zip( img_buffer, &zip_buffer_size ); + img_buffer_size = zip_buffer_size; + break; #else - Error("zlib is required for zipped images. Falling back to raw image"); - type = STREAM_RAW; + Error("zlib is required for zipped images. Falling back to raw image"); + type = STREAM_RAW; #endif // HAVE_ZLIB_H - case STREAM_RAW : - img_buffer = (uint8_t*)(send_image->Buffer()); - img_buffer_size = send_image->Size(); - break; - default: - Fatal( "Unexpected frame type %d", type ); - break; - } - } + case STREAM_RAW : + img_buffer = (uint8_t*)(send_image->Buffer()); + img_buffer_size = send_image->Size(); + break; + default: + Fatal( "Unexpected frame type %d", type ); + break; + } + } - switch( type ) - { - case STREAM_JPEG : - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case STREAM_RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - break; - case STREAM_ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } + switch( type ) + { + case STREAM_JPEG : + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); + break; + case STREAM_RAW : + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); + break; + case STREAM_ZIP : + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); + break; + default : + Fatal( "Unexpected frame type %d", type ); + break; + } - if(send_raw) { + if(send_raw) { #if HAVE_SENDFILE - fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); - if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { - /* sendfile() failed, use standard way instead */ - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } - } + fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); + if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { + /* sendfile() failed, use standard way instead */ + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } + } #else - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } #endif - fclose(fdj); /* Close the file handle */ - } else { - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - } - - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + fclose(fdj); /* Close the file handle */ + } else { + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + } + + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void EventStream::runStream() { - Event::Initialise(); + Event::Initialise(); - openComms(); + openComms(); - checkInitialised(); + checkInitialised(); - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate( (double)event_data->frame_count/event_data->duration ); - if ( type == STREAM_JPEG ) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + if ( type == STREAM_JPEG ) + fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); - if ( !event_data ) - { - sendTextFrame( "No event data found" ); - exit( 0 ); - } + if ( !event_data ) + { + sendTextFrame( "No event data found" ); + exit( 0 ); + } - unsigned int delta_us = 0; - while( !zm_terminate ) - { - gettimeofday( &now, NULL ); + unsigned int delta_us = 0; + while( !zm_terminate ) + { + gettimeofday( &now, NULL ); - while(checkCommandQueue()); + while(checkCommandQueue()); - if ( step != 0 ) - curr_frame_id += step; + if ( step != 0 ) + curr_frame_id += step; - checkEventLoaded(); + checkEventLoaded(); - // Get current frame data - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + // Get current frame data + FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - //Info( "cst:%.2f", curr_stream_time ); - //Info( "cfid:%d", curr_frame_id ); - //Info( "fdt:%d", frame_data->timestamp ); - if ( !paused ) - { - bool in_event = true; - double time_to_event = 0; - if ( replay_rate > 0 ) - { - time_to_event = event_data->frames[0].timestamp - curr_stream_time; - if ( time_to_event > 0 ) - in_event = false; - } - else if ( replay_rate < 0 ) - { - time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; - if ( time_to_event > 0 ) - in_event = false; - } - if ( !in_event ) - { - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > 1 ) - { - static char frame_text[64]; - snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); - if ( !sendTextFrame( frame_text ) ) - zm_terminate = true; - } - //else - //{ - usleep( STREAM_PAUSE_WAIT ); - //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); - curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //} - continue; - } - } + //Info( "cst:%.2f", curr_stream_time ); + //Info( "cfid:%d", curr_frame_id ); + //Info( "fdt:%d", frame_data->timestamp ); + if ( !paused ) + { + bool in_event = true; + double time_to_event = 0; + if ( replay_rate > 0 ) + { + time_to_event = event_data->frames[0].timestamp - curr_stream_time; + if ( time_to_event > 0 ) + in_event = false; + } + else if ( replay_rate < 0 ) + { + time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; + if ( time_to_event > 0 ) + in_event = false; + } + if ( !in_event ) + { + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > 1 ) + { + static char frame_text[64]; + snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); + if ( !sendTextFrame( frame_text ) ) + zm_terminate = true; + } + //else + //{ + usleep( STREAM_PAUSE_WAIT ); + //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); + curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //} + continue; + } + } - // Figure out if we should send this frame - bool send_frame = false; - if ( !paused ) - { - // If we are streaming and this frame is due to be sent - if ( ((curr_frame_id-1)%frame_mod) == 0 ) - { - delta_us = (unsigned int)(frame_data->delta * 1000000); - // if effective > base we should speed up frame delivery - delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); - // but must not exceed maxfps - delta_us = max(delta_us, 1000000 / maxfps); - send_frame = true; - } - } - else if ( step != 0 ) - { - // We are paused and are just stepping forward or backward one frame - step = 0; - send_frame = true; - } - else - { - // We are paused, and doing nothing - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > MAX_STREAM_DELAY ) - { - // Send keepalive - Debug( 2, "Sending keepalive frame" ); - send_frame = true; - } - } + // Figure out if we should send this frame + bool send_frame = false; + if ( !paused ) + { + // If we are streaming and this frame is due to be sent + if ( ((curr_frame_id-1)%frame_mod) == 0 ) + { + delta_us = (unsigned int)(frame_data->delta * 1000000); + // if effective > base we should speed up frame delivery + delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); + // but must not exceed maxfps + delta_us = max(delta_us, 1000000 / maxfps); + send_frame = true; + } + } + else if ( step != 0 ) + { + // We are paused and are just stepping forward or backward one frame + step = 0; + send_frame = true; + } + else + { + // We are paused, and doing nothing + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > MAX_STREAM_DELAY ) + { + // Send keepalive + Debug( 2, "Sending keepalive frame" ); + send_frame = true; + } + } - if ( send_frame ) - if ( !sendFrame( delta_us ) ) - zm_terminate = true; + if ( send_frame ) + if ( !sendFrame( delta_us ) ) + zm_terminate = true; - curr_stream_time = frame_data->timestamp; + curr_stream_time = frame_data->timestamp; - if ( !paused ) - { - curr_frame_id += replay_rate>0?1:-1; - if ( send_frame && type != STREAM_MPEG ) - { - Debug( 3, "dUs: %d", delta_us ); - usleep( delta_us ); - } - } - else - { - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); - } - } + if ( !paused ) + { + curr_frame_id += replay_rate>0?1:-1; + if ( send_frame && type != STREAM_MPEG ) + { + Debug( 3, "dUs: %d", delta_us ); + usleep( delta_us ); + } + } + else + { + usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + } + } #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) - delete vid_stream; + if ( type == STREAM_MPEG ) + delete vid_stream; #endif // HAVE_LIBAVCODEC - closeComms(); + closeComms(); } diff --git a/src/zm_event.h b/src/zm_event.h index 004974c4a..576e0e869 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -37,6 +37,7 @@ #include "zm.h" #include "zm_image.h" #include "zm_stream.h" +#include "zm_video.h" class Zone; class Monitor; @@ -48,22 +49,23 @@ class Monitor; // class Event { -friend class EventStream; + friend class EventStream; -protected: + protected: static bool initialised; static char capture_file_format[PATH_MAX]; static char analyse_file_format[PATH_MAX]; static char general_file_format[PATH_MAX]; + static char video_file_format[PATH_MAX]; -protected: + protected: static int sd; -public: + public: typedef std::set StringSet; typedef std::map StringSetMap; -protected: + protected: typedef enum { NORMAL, BULK, ALARM } FrameType; struct PreAlarmData @@ -77,23 +79,30 @@ protected: static int pre_alarm_count; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; -protected: + protected: unsigned int id; Monitor *monitor; struct timeval start_time; struct timeval end_time; - std::string cause; - StringSetMap noteSetMap; + std::string cause; + StringSetMap noteSetMap; + bool videoEvent; int frames; int alarm_frames; unsigned int tot_score; unsigned int max_score; char path[PATH_MAX]; + VideoWriter* videowriter; + FILE* timecodes_fd; + char video_name[PATH_MAX]; + char video_file[PATH_MAX]; + char timecodes_name[PATH_MAX]; + char timecodes_file[PATH_MAX]; -protected: + protected: int last_db_frame; -protected: + protected: static void Initialise() { if ( initialised ) @@ -102,18 +111,19 @@ protected: snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); + snprintf( video_file_format, sizeof(video_file_format), "%%s/%%s"); initialised = true; } void createNotes( std::string ¬es ); -public: + public: static bool OpenFrameSocket( int ); static bool ValidateFrameSocket( int ); -public: - Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); + public: + Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false ); ~Event(); int Id() const { return( id ); } @@ -127,16 +137,17 @@ public: bool SendFrameImage( const Image *image, bool alarm_frame=false ); bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); + bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow ); void updateNotes( const StringSetMap &stringSetMap ); void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); -private: + private: void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); -public: + public: static const char *getSubPath( struct tm *time ) { static char subpath[PATH_MAX] = ""; @@ -148,7 +159,11 @@ public: return( Event::getSubPath( localtime( time ) ) ); } -public: + char* getEventFile(void){ + return video_file; + } + + public: static int PreAlarmCount() { return( pre_alarm_count ); @@ -189,104 +204,84 @@ public: class EventStream : public StreamBase { -public: - typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; + public: + typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; -protected: -<<<<<<< HEAD + protected: struct FrameData { - //unsigned long id; - time_t timestamp; - time_t offset; - double delta; - bool in_db; + //unsigned long id; + time_t timestamp; + time_t offset; + double delta; + bool in_db; }; struct EventData { - unsigned long event_id; - unsigned long monitor_id; - unsigned long storage_id; - unsigned long frame_count; - time_t start_time; - double duration; - char path[PATH_MAX]; - int n_frames; - FrameData *frames; + unsigned long event_id; + unsigned long monitor_id; + unsigned long storage_id; + unsigned long frame_count; + time_t start_time; + double duration; + char path[PATH_MAX]; + int n_frames; + FrameData *frames; + char video_file[PATH_MAX]; }; -======= - struct FrameData { - //unsigned long id; - time_t timestamp; - time_t offset; - double delta; - bool in_db; - }; - struct EventData - { - unsigned long event_id; - unsigned long monitor_id; - unsigned long frame_count; - time_t start_time; - double duration; - char path[PATH_MAX]; - int n_frames; - FrameData *frames; - }; ->>>>>>> master + protected: + static const int STREAM_PAUSE_WAIT = 250000; // Microseconds -protected: - static const int STREAM_PAUSE_WAIT = 250000; // Microseconds + static const StreamMode DEFAULT_MODE = MODE_SINGLE; - static const StreamMode DEFAULT_MODE = MODE_SINGLE; + protected: + StreamMode mode; + bool forceEventChange; -protected: - StreamMode mode; - bool forceEventChange; + protected: + int curr_frame_id; + double curr_stream_time; -protected: - int curr_frame_id; - double curr_stream_time; + EventData *event_data; - EventData *event_data; + protected: + bool loadEventData( int event_id ); + bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); + bool loadInitialEventData( int monitor_id, time_t event_time ); -protected: - bool loadEventData( int event_id ); - bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); - bool loadInitialEventData( int monitor_id, time_t event_time ); + void checkEventLoaded(); + void processCommand( const CmdMsg *msg ); + bool sendFrame( int delta_us ); - void checkEventLoaded(); - void processCommand( const CmdMsg *msg ); - bool sendFrame( int delta_us ); + public: + EventStream() + { + mode = DEFAULT_MODE; -public: - EventStream() - { - mode = DEFAULT_MODE; + forceEventChange = false; - forceEventChange = false; + curr_frame_id = 0; + curr_stream_time = 0.0; - curr_frame_id = 0; - curr_stream_time = 0.0; - - event_data = 0; - } - void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) - { - loadInitialEventData( init_event_id, init_frame_id ); - loadMonitor( event_data->monitor_id ); - } - void setStreamStart( int monitor_id, time_t event_time ) - { - loadInitialEventData( monitor_id, event_time ); - loadMonitor( monitor_id ); - } - void setStreamMode( StreamMode p_mode ) - { - mode = p_mode; - } - void runStream(); + event_data = 0; + } + void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) + { + loadInitialEventData( init_event_id, init_frame_id ); + loadMonitor( event_data->monitor_id ); + } + void setStreamStart( int monitor_id, time_t event_time ) + { + loadInitialEventData( monitor_id, event_time ); + loadMonitor( monitor_id ); + } + void setStreamMode( StreamMode p_mode ) + { + mode = p_mode; + } + void runStream(); + Image *getImage(); }; #endif // ZM_EVENT_H diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index fc20f6286..fdf35e862 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -23,213 +23,415 @@ #if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE +void FFMPEGInit() { + static bool bInit = false; + + if(!bInit) { + av_register_all(); + av_log_set_level(AV_LOG_DEBUG); + bInit = true; + } +} + #if HAVE_LIBAVUTIL enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) { - enum _AVPIXELFORMAT pf; + enum _AVPIXELFORMAT pf; - Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); + Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); - switch(p_colours) { - case ZM_COLOUR_RGB24: - { - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - pf = AV_PIX_FMT_BGR24; - } else { - /* Assume RGB subpixel order */ - pf = AV_PIX_FMT_RGB24; - } - break; - } - case ZM_COLOUR_RGB32: - { - if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - pf = AV_PIX_FMT_ARGB; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - pf = AV_PIX_FMT_ABGR; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - pf = AV_PIX_FMT_BGRA; - } else { - /* Assume RGBA subpixel order */ - pf = AV_PIX_FMT_RGBA; - } - break; - } - case ZM_COLOUR_GRAY8: - pf = AV_PIX_FMT_GRAY8; - break; - default: - Panic("Unexpected colours: %d",p_colours); - pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ - break; - } + switch(p_colours) { + case ZM_COLOUR_RGB24: + { + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + pf = AV_PIX_FMT_BGR24; + } else { + /* Assume RGB subpixel order */ + pf = AV_PIX_FMT_RGB24; + } + break; + } + case ZM_COLOUR_RGB32: + { + if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + pf = AV_PIX_FMT_ARGB; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + pf = AV_PIX_FMT_ABGR; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + pf = AV_PIX_FMT_BGRA; + } else { + /* Assume RGBA subpixel order */ + pf = AV_PIX_FMT_RGBA; + } + break; + } + case ZM_COLOUR_GRAY8: + pf = AV_PIX_FMT_GRAY8; + break; + default: + Panic("Unexpected colours: %d",p_colours); + pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ + break; + } - return pf; + return pf; } #endif // HAVE_LIBAVUTIL #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { - Debug(4,"SWScale object created"); + Debug(4,"SWScale object created"); - /* Allocate AVFrame for the input */ + /* Allocate AVFrame for the input */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - input_avframe = av_frame_alloc(); + input_avframe = av_frame_alloc(); #else - input_avframe = avcodec_alloc_frame(); + input_avframe = avcodec_alloc_frame(); #endif - if(input_avframe == NULL) { - Fatal("Failed allocating AVFrame for the input"); - } + if(input_avframe == NULL) { + Fatal("Failed allocating AVFrame for the input"); + } - /* Allocate AVFrame for the output */ + /* Allocate AVFrame for the output */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - output_avframe = av_frame_alloc(); + output_avframe = av_frame_alloc(); #else - output_avframe = avcodec_alloc_frame(); + output_avframe = avcodec_alloc_frame(); #endif - if(output_avframe == NULL) { - Fatal("Failed allocating AVFrame for the output"); - } + if(output_avframe == NULL) { + Fatal("Failed allocating AVFrame for the output"); + } } SWScale::~SWScale() { - /* Free up everything */ + /* Free up everything */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &input_avframe ); + av_frame_free( &input_avframe ); #else - av_freep( &input_avframe ); + av_freep( &input_avframe ); #endif - //input_avframe = NULL; + //input_avframe = NULL; #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &output_avframe ); + av_frame_free( &output_avframe ); #else - av_freep( &output_avframe ); + av_freep( &output_avframe ); #endif - //output_avframe = NULL; + //output_avframe = NULL; - if(swscale_ctx) { - sws_freeContext(swscale_ctx); - swscale_ctx = NULL; - } - - Debug(4,"SWScale object destroyed"); + if(swscale_ctx) { + sws_freeContext(swscale_ctx); + swscale_ctx = NULL; + } + + Debug(4,"SWScale object destroyed"); } int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - /* Assign the defaults */ - default_input_pf = in_pf; - default_output_pf = out_pf; - default_width = width; - default_height = height; + /* Assign the defaults */ + default_input_pf = in_pf; + default_output_pf = out_pf; + default_width = width; + default_height = height; - gotdefaults = true; + gotdefaults = true; - return 0; + return 0; } int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - /* Parameter checking */ - if(in_buffer == NULL || out_buffer == NULL) { - Error("NULL Input or output buffer"); - return -1; - } - if(in_pf == 0 || out_pf == 0) { - Error("Invalid input or output pixel formats"); - return -2; - } - if(!width || !height) { - Error("Invalid width or height"); - return -3; - } + /* Parameter checking */ + if(in_buffer == NULL || out_buffer == NULL) { + Error("NULL Input or output buffer"); + return -1; + } + + if(!width || !height) { + Error("Invalid width or height"); + return -3; + } #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) - /* Warn if the input or output pixelformat is not supported */ - if(!sws_isSupportedInput(in_pf)) { - Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); - } - if(!sws_isSupportedOutput(out_pf)) { - Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); - } + /* Warn if the input or output pixelformat is not supported */ + if(!sws_isSupportedInput(in_pf)) { + Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); + } + if(!sws_isSupportedOutput(out_pf)) { + Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); + } #endif - /* Check the buffer sizes */ - size_t insize = avpicture_get_size(in_pf, width, height); - if(insize != in_buffer_size) { - Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); - return -4; - } - size_t outsize = avpicture_get_size(out_pf, width, height); - if(outsize < out_buffer_size) { - Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); - return -5; - } + /* Check the buffer sizes */ +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + size_t insize = av_image_get_buffer_size(in_pf, width, height,1); +#else + size_t insize = avpicture_get_size(in_pf, width, height); +#endif + if(insize != in_buffer_size) { + Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); + return -4; + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + size_t outsize = av_image_get_buffer_size(out_pf, width, height,1); +#else + size_t outsize = avpicture_get_size(out_pf, width, height); +#endif - /* Get the context */ - swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL ); - if(swscale_ctx == NULL) { - Error("Failed getting swscale context"); - return -6; - } + if(outsize < out_buffer_size) { + Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); + return -5; + } - /* Fill in the buffers */ - if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) { - Error("Failed filling input frame with input buffer"); - return -7; - } - if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) { - Error("Failed filling output frame with output buffer"); - return -8; - } + /* Get the context */ + swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); + if(swscale_ctx == NULL) { + Error("Failed getting swscale context"); + return -6; + } - /* Do the conversion */ - if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { - Error("swscale conversion failed"); - return -10; - } + /* Fill in the buffers */ +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + if(av_image_fill_arrays(input_avframe->data, input_avframe->linesize, + (uint8_t*)in_buffer, in_pf, width, height, 1) <= 0) + { +#else + if(avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, + in_pf, width, height ) <= 0) + { +#endif + Error("Failed filling input frame with input buffer"); + return -7; + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + if(av_image_fill_arrays(output_avframe->data, output_avframe->linesize, + out_buffer, out_pf, width, height, 1) <= 0) + { +#else + if(avpicture_fill( (AVPicture*)output_avframe, out_buffer, + out_pf, width, height ) <= 0) + { +#endif + Error("Failed filling output frame with output buffer"); + return -8; + } - return 0; + /* Do the conversion */ + if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { + Error("swscale conversion failed"); + return -10; + } + + return 0; } int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - if(img->Width() != width) { - Error("Source image width differs. Source: %d Output: %d",img->Width(), width); - return -12; - } + if(img->Width() != width) { + Error("Source image width differs. Source: %d Output: %d",img->Width(), width); + return -12; + } - if(img->Height() != height) { - Error("Source image height differs. Source: %d Output: %d",img->Height(), height); - return -13; - } + if(img->Height() != height) { + Error("Source image height differs. Source: %d Output: %d",img->Height(), height); + return -13; + } - return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); + return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); } int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } + if(!gotdefaults) { + Error("Defaults are not set"); + return -24; + } - return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); + return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } + if(!gotdefaults) { + Error("Defaults are not set"); + return -24; + } - return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); + return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE + +#if HAVE_LIBAVUTIL +int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb){ + int64_t a, b, this_thing; + + av_assert0(in_ts != AV_NOPTS_VALUE); + av_assert0(duration >= 0); + + if (*last == AV_NOPTS_VALUE || !duration || in_tb.num*(int64_t)out_tb.den <= out_tb.num*(int64_t)in_tb.den) { +simple_round: + *last = av_rescale_q(in_ts, in_tb, fs_tb) + duration; + return av_rescale_q(in_ts, in_tb, out_tb); + } + + a = av_rescale_q_rnd(2*in_ts-1, in_tb, fs_tb, AV_ROUND_DOWN) >>1; + b = (av_rescale_q_rnd(2*in_ts+1, in_tb, fs_tb, AV_ROUND_UP )+1)>>1; + if (*last < 2*a - b || *last > 2*b - a) + goto simple_round; + + this_thing = av_clip64(*last, a, b); + *last = this_thing + duration; + + return av_rescale_q(this_thing, fs_tb, out_tb); +} +#endif + +int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename) { + AVFormatContext *s = avformat_alloc_context(); + int ret = 0; + + *avctx = NULL; + if (!s) { + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); + return ret; + } + + if (!oformat) { + if (format) { + oformat = av_guess_format(format, NULL, NULL); + if (!oformat) { + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + ret = AVERROR(EINVAL); + } + } else { + oformat = av_guess_format(NULL, filename, NULL); + if (!oformat) { + ret = AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", filename); + } + } + } + + if (ret) { + avformat_free_context(s); + return ret; + } else { + s->oformat = oformat; + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (s->priv_data) { + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data= s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } + } else { + av_log(s, AV_LOG_ERROR, "Out of memory\n"); + ret = AVERROR(ENOMEM); + return ret; + } + s->priv_data = NULL; + } + + if (filename) strncpy(s->filename, filename, sizeof(s->filename)); + *avctx = s; + return 0; + } +} + +static void zm_log_fps(double d, const char *postfix) +{ + uint64_t v = lrintf(d * 100); + if (!v) { + Debug(3, "%1.4f %s", d, postfix); + } else if (v % 100) { + Debug(3, "%3.2f %s", d, postfix); + } else if (v % (100 * 1000)) { + Debug(3, "%1.0f %s", d, postfix); + } else + Debug(3, "%1.0fk %s", d / 1000, postfix); +} + +/* "user interface" functions */ +void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) { + char buf[256]; + int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); + AVStream *st = ic->streams[i]; + AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); + char separator = '\n'; + + avcodec_string(buf, sizeof(buf), st->codec, is_output); + Debug(3, " Stream #%d:%d", index, i); + + /* the pid is an important information, so we display it */ + /* XXX: add a generic system */ + if (flags & AVFMT_SHOW_IDS) + Debug(3, "[0x%x]", st->id); + if (lang) + Debug(3, "(%s)", lang->value); + av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d", st->codec_info_nb_frames, + st->time_base.num, st->time_base.den); + Debug(3, ": %s", buf); + + if (st->sample_aspect_ratio.num && // default + av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) { + AVRational display_aspect_ratio; + av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, + st->codec->width * (int64_t)st->sample_aspect_ratio.num, + st->codec->height * (int64_t)st->sample_aspect_ratio.den, + 1024 * 1024); + Debug(3, ", SAR %d:%d DAR %d:%d", + st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, + display_aspect_ratio.num, display_aspect_ratio.den); + } + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + int fps = st->avg_frame_rate.den && st->avg_frame_rate.num; + int tbr = st->r_frame_rate.den && st->r_frame_rate.num; + int tbn = st->time_base.den && st->time_base.num; + int tbc = st->codec->time_base.den && st->codec->time_base.num; + + if (fps || tbr || tbn || tbc) + Debug(3, "%s", separator); + + if (fps) + zm_log_fps(av_q2d(st->avg_frame_rate), tbr || tbn || tbc ? "fps, " : "fps"); + if (tbr) + zm_log_fps(av_q2d(st->r_frame_rate), tbn || tbc ? "tbr, " : "tbr"); + if (tbn) + zm_log_fps(1 / av_q2d(st->time_base), tbc ? "tbn, " : "tbn"); + if (tbc) + zm_log_fps(1 / av_q2d(st->codec->time_base), "tbc"); + } + + if (st->disposition & AV_DISPOSITION_DEFAULT) + Debug(3, " (default)"); + if (st->disposition & AV_DISPOSITION_DUB) + Debug(3, " (dub)"); + if (st->disposition & AV_DISPOSITION_ORIGINAL) + Debug(3, " (original)"); + if (st->disposition & AV_DISPOSITION_COMMENT) + Debug(3, " (comment)"); + if (st->disposition & AV_DISPOSITION_LYRICS) + Debug(3, " (lyrics)"); + if (st->disposition & AV_DISPOSITION_KARAOKE) + Debug(3, " (karaoke)"); + if (st->disposition & AV_DISPOSITION_FORCED) + Debug(3, " (forced)"); + if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) + Debug(3, " (hearing impaired)"); + if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED) + Debug(3, " (visual impaired)"); + if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS) + Debug(3, " (clean effects)"); + Debug(3, "\n"); + + //dump_metadata(NULL, st->metadata, " "); + + //dump_sidedata(NULL, st, " "); + } diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 507e5cf4b..ca74af2f8 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -29,6 +29,7 @@ extern "C" { // AVUTIL #if HAVE_LIBAVUTIL_AVUTIL_H +#include "libavutil/avassert.h" #include #include #include @@ -39,69 +40,75 @@ extern "C" { * b and c the minor and micro versions of libav * d and e the minor and micro versions of FFmpeg */ #define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \ - ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ - (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) #if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0) #include #else #include #endif + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) +#include +#else +#include +#endif #elif HAVE_FFMPEG_AVUTIL_H #include #include #include #include #endif /* HAVE_LIBAVUTIL_AVUTIL_H */ - + #if defined(HAVE_LIBAVUTIL_AVUTIL_H) #if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100) - #define _AVPIXELFORMAT AVPixelFormat + #define _AVPIXELFORMAT AVPixelFormat #else - #define _AVPIXELFORMAT PixelFormat - #define AV_PIX_FMT_NONE PIX_FMT_NONE - #define AV_PIX_FMT_RGB444 PIX_FMT_RGB444 - #define AV_PIX_FMT_RGB555 PIX_FMT_RGB555 - #define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 - #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 - #define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 - #define AV_PIX_FMT_BGRA PIX_FMT_BGRA - #define AV_PIX_FMT_ARGB PIX_FMT_ARGB - #define AV_PIX_FMT_ABGR PIX_FMT_ABGR - #define AV_PIX_FMT_RGBA PIX_FMT_RGBA - #define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8 - #define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422 - #define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P - #define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P - #define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P - #define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P - #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P - #define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P - #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 - #define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P - #define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P - #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 - #define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411 - #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 - #define AV_PIX_FMT_BGR555 PIX_FMT_BGR555 - #define AV_PIX_FMT_BGR8 PIX_FMT_BGR8 - #define AV_PIX_FMT_BGR4 PIX_FMT_BGR4 - #define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE - #define AV_PIX_FMT_RGB8 PIX_FMT_RGB8 - #define AV_PIX_FMT_RGB4 PIX_FMT_RGB4 - #define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE - #define AV_PIX_FMT_NV12 PIX_FMT_NV12 - #define AV_PIX_FMT_NV21 PIX_FMT_NV21 - #define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1 - #define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1 - #define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE - #define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE - #define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P - #define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P - #define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P - //#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264 - //#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1 - //#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2 + #define _AVPIXELFORMAT PixelFormat + #define AV_PIX_FMT_NONE PIX_FMT_NONE + #define AV_PIX_FMT_RGB444 PIX_FMT_RGB444 + #define AV_PIX_FMT_RGB555 PIX_FMT_RGB555 + #define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 + #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 + #define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 + #define AV_PIX_FMT_BGRA PIX_FMT_BGRA + #define AV_PIX_FMT_ARGB PIX_FMT_ARGB + #define AV_PIX_FMT_ABGR PIX_FMT_ABGR + #define AV_PIX_FMT_RGBA PIX_FMT_RGBA + #define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8 + #define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422 + #define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P + #define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P + #define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P + #define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P + #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P + #define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P + #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 + #define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P + #define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P + #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 + #define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411 + #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 + #define AV_PIX_FMT_BGR555 PIX_FMT_BGR555 + #define AV_PIX_FMT_BGR8 PIX_FMT_BGR8 + #define AV_PIX_FMT_BGR4 PIX_FMT_BGR4 + #define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE + #define AV_PIX_FMT_RGB8 PIX_FMT_RGB8 + #define AV_PIX_FMT_RGB4 PIX_FMT_RGB4 + #define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE + #define AV_PIX_FMT_NV12 PIX_FMT_NV12 + #define AV_PIX_FMT_NV21 PIX_FMT_NV21 + #define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1 + #define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1 + #define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE + #define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE + #define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P + #define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P + #define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P + //#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264 + //#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1 + //#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2 #endif #endif /* HAVE_LIBAVUTIL_AVUTIL_H */ @@ -116,8 +123,8 @@ extern "C" { * b and c the minor and micro versions of libav * d and e the minor and micro versions of FFmpeg */ #define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \ - ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ - (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) #elif HAVE_FFMPEG_AVCODEC_H #include @@ -125,9 +132,9 @@ extern "C" { #if defined(HAVE_LIBAVCODEC_AVCODEC_H) #if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100) - #define _AVCODECID AVCodecID + #define _AVCODECID AVCodecID #else - #define _AVCODECID CodecID + #define _AVCODECID CodecID #endif #endif /* HAVE_LIBAVCODEC_AVCODEC_H */ @@ -141,8 +148,8 @@ extern "C" { * b and c the minor and micro versions of libav * d and e the minor and micro versions of FFmpeg */ #define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \ - ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ - (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) #elif HAVE_FFMPEG_AVFORMAT_H #include @@ -157,8 +164,8 @@ extern "C" { * b and c the minor and micro versions of libav * d and e the minor and micro versions of FFmpeg */ #define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \ - ( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ - (LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + ( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) #elif HAVE_FFMPEG_AVDEVICE_H #include @@ -173,8 +180,8 @@ extern "C" { * b and c the minor and micro versions of libav * d and e the minor and micro versions of FFmpeg */ #define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \ - ( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ - (LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + ( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) #elif HAVE_FFMPEG_SWSCALE_H #include @@ -194,6 +201,9 @@ extern "C" { #endif #endif +/* A single function to initialize ffmpeg, to avoid multiple initializations */ +void FFMPEGInit(); + #if HAVE_LIBAVUTIL enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder); #endif // HAVE_LIBAVUTIL @@ -203,23 +213,23 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL class SWScale { public: - SWScale(); - ~SWScale(); - int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); - int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); - int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); + SWScale(); + ~SWScale(); + int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); + int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); + int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); + int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); + int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); protected: - bool gotdefaults; - struct SwsContext* swscale_ctx; - AVFrame* input_avframe; - AVFrame* output_avframe; - enum _AVPIXELFORMAT default_input_pf; - enum _AVPIXELFORMAT default_output_pf; - unsigned int default_width; - unsigned int default_height; + bool gotdefaults; + struct SwsContext* swscale_ctx; + AVFrame* input_avframe; + AVFrame* output_avframe; + enum _AVPIXELFORMAT default_input_pf; + enum _AVPIXELFORMAT default_output_pf; + unsigned int default_width; + unsigned int default_height; }; #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL @@ -256,23 +266,55 @@ protected: */ #ifdef __cplusplus - inline static const std::string av_make_error_string(int errnum) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; + inline static const std::string av_make_error_string(int errnum) + { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; #if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) - av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); + av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); #else - snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum); + snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum); #endif - return (std::string)errbuf; - } + return (std::string)errbuf; + } - #undef av_err2str - #define av_err2str(errnum) av_make_error_string(errnum).c_str() + #undef av_err2str + #define av_err2str(errnum) av_make_error_string(errnum).c_str() - #endif // __cplusplus + #endif // __cplusplus #endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) +#ifndef avformat_alloc_output_context2 +int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename); +#define avformat_alloc_output_context2(x,y,z,a) hacked_up_context2_for_older_ffmpeg(x,y,z,a) +#endif + +#ifndef av_rescale_delta +/** + * Rescale a timestamp while preserving known durations. + */ +int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb); +#endif + +#ifndef av_clip64 +/** + * Clip a signed 64bit integer value into the amin-amax range. + * @param a value to clip + * @param amin minimum value of the clip range + * @param amax maximum value of the clip range + * @return clipped value + */ +static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax) +{ + if (a < amin) return amin; + else if (a > amax) return amax; + else return a; +} + +#define av_clip64 av_clip64_c +#endif + +void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output); + #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 1b35c6a0a..a6ca38e67 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -23,76 +23,84 @@ #include "zm_ffmpeg_camera.h" +extern "C"{ +#include "libavutil/time.h" +} #ifndef AV_ERROR_MAX_STRING_SIZE #define AV_ERROR_MAX_STRING_SIZE 64 #endif #ifdef SOLARIS -#include // for ESRCH +#include // for ESRCH #include #include #endif -FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), - mMethod( p_method ), - mOptions( p_options ) +FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + mPath( p_path ), + mMethod( p_method ), + mOptions( p_options ) { - if ( capture ) - { - Initialise(); - } - - mFormatContext = NULL; - mVideoStreamId = -1; - mCodecContext = NULL; - mCodec = NULL; - mRawFrame = NULL; - mFrame = NULL; - frameCount = 0; - mIsOpening = false; - mCanCapture = false; - mOpenStart = 0; - mReopenThread = 0; - -#if HAVE_LIBSWSCALE - mConvertContext = NULL; + if ( capture ) + { + Initialise(); + } + + mFormatContext = NULL; + mVideoStreamId = -1; + mAudioStreamId = -1; + mCodecContext = NULL; + mCodec = NULL; + mRawFrame = NULL; + mFrame = NULL; + frameCount = 0; + startTime=0; + mIsOpening = false; + mCanCapture = false; + mOpenStart = 0; + mReopenThread = 0; + wasRecording = false; + videoStore = NULL; + +#if HAVE_LIBSWSCALE + mConvertContext = NULL; #endif - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = AV_PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + } FfmpegCamera::~FfmpegCamera() { - CloseFfmpeg(); + CloseFfmpeg(); - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void FfmpegCamera::Initialise() { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); - av_register_all(); + av_register_all(); + avformat_network_init(); } void FfmpegCamera::Terminate() @@ -101,373 +109,587 @@ void FfmpegCamera::Terminate() int FfmpegCamera::PrimeCapture() { - Info( "Priming capture from %s", mPath.c_str() ); + mVideoStreamId = -1; + mAudioStreamId = -1; + Info( "Priming capture from %s", mPath.c_str() ); - if (OpenFfmpeg() != 0){ - ReopenFfmpeg(); - } - return 0; + if (OpenFfmpeg() != 0){ + ReopenFfmpeg(); + } + return 0; } int FfmpegCamera::PreCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int FfmpegCamera::Capture( Image &image ) { - if (!mCanCapture){ - return -1; - } - - // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. - if (mReopenThread != 0) { - void *retval = 0; - int ret; - - ret = pthread_join(mReopenThread, &retval); - if (ret != 0){ - Error("Could not join reopen thread."); + if (!mCanCapture){ + return -1; } - Info( "Successfully reopened stream." ); - mReopenThread = 0; - } + // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. + if (mReopenThread != 0) { + void *retval = 0; + int ret; + + ret = pthread_join(mReopenThread, &retval); + if (ret != 0){ + Error("Could not join reopen thread."); + } + + Info( "Successfully reopened stream." ); + mReopenThread = 0; + } - AVPacket packet; - uint8_t* directbuffer; + AVPacket packet; + 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."); - return (-1); - } - - int frameComplete = false; - while ( !frameComplete ) - { - int avResult = av_read_frame( mFormatContext, &packet ); - if ( avResult < 0 ) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); - if ( - // Check if EOF. - (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || - // Check for Connection failure. - (avResult == -110) - ) - { - Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); - ReopenFfmpeg(); - } - - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); - return( -1 ); - } - Debug( 5, "Got packet from stream %d", packet.stream_index ); - if ( packet.stream_index == mVideoStreamId ) - { -#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) - if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) -#else - if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) -#endif - Fatal( "Unable to decode frame at frame %d", frameCount ); - - Debug( 4, "Decoded video packet at frame %d", frameCount ); - - if ( frameComplete ) - { - Debug( 3, "Got frame %d", frameCount ); - - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); + /* 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."); + return (-1); + } -#if HAVE_LIBSWSCALE - if(mConvertContext == NULL) { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + int frameComplete = false; + while ( !frameComplete ) + { + int avResult = av_read_frame( mFormatContext, &packet ); + if ( avResult < 0 ) + { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); + if ( + // Check if EOF. + (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || + // Check for Connection failure. + (avResult == -110) + ) + { + Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf ); + ReopenFfmpeg(); + } - if(mConvertContext == NULL) - Fatal( "Unable to create conversion context for %s", mPath.c_str() ); - } - - if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) - Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); -#else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); -#endif // HAVE_LIBSWSCALE - - frameCount++; - } - } -#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) - av_packet_unref( &packet); + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); + return( -1 ); + } + Debug( 5, "Got packet from stream %d", packet.stream_index ); + // What about audio stream? Maybe someday we could do sound detection... + if ( packet.stream_index == mVideoStreamId ) + { +#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) + if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) #else - av_free_packet( &packet ); + if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) #endif - } - return (0); -} + Fatal( "Unable to decode frame at frame %d", frameCount ); + + Debug( 4, "Decoded video packet at frame %d", frameCount ); + + if ( frameComplete ) { + Debug( 4, "Got frame %d", frameCount ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(mFrame->data, mFrame->linesize, + directbuffer, imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)mFrame, directbuffer, + imagePixFormat, width, height); +#endif + +#if HAVE_LIBSWSCALE + if(mConvertContext == NULL) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if(mConvertContext == NULL) + Fatal( "Unable to create conversion context for %s", mPath.c_str() ); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); +#else // HAVE_LIBSWSCALE + Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); +#endif // HAVE_LIBSWSCALE + + frameCount++; + } // end if frameComplete + } else { + Debug( 4, "Different stream_index %d", packet.stream_index ); + } // end if packet.stream_index == mVideoStreamId +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( &packet); +#else + av_free_packet( &packet ); +#endif + } // end while ! frameComplete + return (0); +} // FfmpegCamera::Capture int FfmpegCamera::PostCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int FfmpegCamera::OpenFfmpeg() { - Debug ( 2, "OpenFfmpeg called." ); + Debug ( 2, "OpenFfmpeg called." ); - mOpenStart = time(NULL); - mIsOpening = true; + mOpenStart = time(NULL); + mIsOpening = true; - // Open the input, not necessarily a file + // Open the input, not necessarily a file #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) - Debug ( 1, "Calling av_open_input_file" ); - if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) + Debug ( 1, "Calling av_open_input_file" ); + if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) #else - // Handle options - AVDictionary *opts = 0; - StringVector opVect = split(Options(), ","); - - // Set transport method as specified by method field, rtpUni is default - if ( Method() == "rtpMulti" ) - opVect.push_back("rtsp_transport=udp_multicast"); - else if ( Method() == "rtpRtsp" ) - opVect.push_back("rtsp_transport=tcp"); - else if ( Method() == "rtpRtspHttp" ) - opVect.push_back("rtsp_transport=http"); - - Debug(2, "Number of Options: %d",opVect.size()); - for (size_t i=0; i 1) { - parts[0] = trimSpaces(parts[0]); - parts[1] = trimSpaces(parts[1]); - if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) { - Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str()); - } - else - { - Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() ); - } - - } - else - { - Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() ); - } - } - Debug ( 1, "Calling avformat_open_input" ); + // Handle options + AVDictionary *opts = 0; + StringVector opVect = split(Options(), ","); + + // Set transport method as specified by method field, rtpUni is default + if ( Method() == "rtpMulti" ) + opVect.push_back("rtsp_transport=udp_multicast"); + else if ( Method() == "rtpRtsp" ) + opVect.push_back("rtsp_transport=tcp"); + else if ( Method() == "rtpRtspHttp" ) + opVect.push_back("rtsp_transport=http"); + + Debug(2, "Number of Options: %d",opVect.size()); + for (size_t i=0; i 1) { + parts[0] = trimSpaces(parts[0]); + parts[1] = trimSpaces(parts[1]); + if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) { + Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str()); + } + else + { + Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() ); + } + + } + else + { + Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() ); + } + } + Debug ( 1, "Calling avformat_open_input" ); - mFormatContext = avformat_alloc_context( ); - mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; - mFormatContext->interrupt_callback.opaque = this; + mFormatContext = avformat_alloc_context( ); + mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; + mFormatContext->interrupt_callback.opaque = this; - if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 ) -#endif - { - mIsOpening = false; - Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); - return -1; - } - - AVDictionaryEntry *e; - if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) { - Warning( "Option %s not recognized by ffmpeg", e->key); - } - - mIsOpening = false; - Debug ( 1, "Opened input" ); - - // 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 ) -#else - Debug ( 1, "Calling avformat_find_stream_info" ); - if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) -#endif - Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); - - Debug ( 1, "Got stream info" ); - - // Find first video stream present - mVideoStreamId = -1; - for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) - { -#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) -#else - if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) + if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 ) #endif { - mVideoStreamId = i; - break; + mIsOpening = false; + Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); + return -1; } - } - if ( mVideoStreamId == -1 ) - Fatal( "Unable to locate video stream in %s", mPath.c_str() ); - Debug ( 1, "Found video stream" ); + AVDictionaryEntry *e; + if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) { + Warning( "Option %s not recognized by ffmpeg", e->key); + } - mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; + mIsOpening = false; + Debug ( 1, "Opened input" ); - // Try and get the codec from the codec context - if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) - Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); + Info( "Stream open %s", mPath.c_str() ); + startTime=av_gettime();//FIXME here or after find_Stream_info + + //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 ) +#else + Debug ( 1, "Calling avformat_find_stream_info" ); + if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) +#endif + Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); - Debug ( 1, "Found decoder" ); + Debug ( 1, "Got stream info" ); - // Open the codec + // Find first video stream present + // The one we want Might not be the first + mVideoStreamId = -1; + mAudioStreamId = -1; + for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) + { +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) +#else + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) +#endif + { + if ( mVideoStreamId == -1 ) { + mVideoStreamId = i; + // if we break, then we won't find the audio stream + continue; + } else { + Debug(2, "Have another video stream." ); + } + } +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) +#else + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO ) +#endif + { + if ( mAudioStreamId == -1 ) { + mAudioStreamId = i; + } else { + Debug(2, "Have another audio stream." ); + } + } + } + if ( mVideoStreamId == -1 ) + Fatal( "Unable to locate video stream in %s", mPath.c_str() ); + if ( mAudioStreamId == -1 ) + Debug( 2, "Unable to locate audio stream in %s", mPath.c_str() ); + + Debug ( 3, "Found video stream at index %d", mVideoStreamId ); + Debug ( 3, "Found audio stream at index %d", mAudioStreamId ); + + mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; + + // Try and get the codec from the codec context + if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) + Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); + + Debug ( 1, "Found decoder" ); + zm_dump_stream_format( mFormatContext, mVideoStreamId, 0, 0 ); + + // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - Debug ( 1, "Calling avcodec_open" ); - if ( avcodec_open( mCodecContext, mCodec ) < 0 ) + Debug ( 1, "Calling avcodec_open" ); + if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else - Debug ( 1, "Calling avcodec_open2" ); - if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) + Debug ( 1, "Calling avcodec_open2" ); + if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif - Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); + Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); - Debug ( 1, "Opened codec" ); + Debug ( 1, "Opened codec" ); - // Allocate space for the native video frame + // Allocate space for the native video frame #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mRawFrame = av_frame_alloc(); + mRawFrame = av_frame_alloc(); #else - mRawFrame = avcodec_alloc_frame(); + mRawFrame = avcodec_alloc_frame(); #endif - // Allocate space for the converted video frame + // Allocate space for the converted video frame #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mFrame = av_frame_alloc(); + mFrame = av_frame_alloc(); #else - mFrame = avcodec_alloc_frame(); + mFrame = avcodec_alloc_frame(); #endif - if(mRawFrame == NULL || mFrame == NULL) - Fatal( "Unable to allocate frame for %s", mPath.c_str() ); + if(mRawFrame == NULL || mFrame == NULL) + Fatal( "Unable to allocate frame for %s", mPath.c_str() ); - Debug ( 1, "Allocated frames" ); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } + Debug ( 1, "Allocated frames" ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } + + Debug ( 1, "Validated imagesize %d", pSize ); - Debug ( 1, "Validated imagesize" ); - #if HAVE_LIBSWSCALE - Debug ( 1, "Calling sws_isSupportedInput" ); - if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { - Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); - } - - if(!sws_isSupportedOutput(imagePixFormat)) { - Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - } - + Debug ( 1, "Calling sws_isSupportedInput" ); + if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { + Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); + } + + if(!sws_isSupportedOutput(imagePixFormat)) { + Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + } + #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE - mCanCapture = true; + mCanCapture = true; - return 0; + return 0; } int FfmpegCamera::ReopenFfmpeg() { - Debug(2, "ReopenFfmpeg called."); + Debug(2, "ReopenFfmpeg called."); - mCanCapture = false; - if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){ - // Log a fatal error and exit the process. - Fatal( "ReopenFfmpeg failed to create worker thread." ); - } + mCanCapture = false; + if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){ + // Log a fatal error and exit the process. + Fatal( "ReopenFfmpeg failed to create worker thread." ); + } - return 0; + return 0; } int FfmpegCamera::CloseFfmpeg(){ - Debug(2, "CloseFfmpeg called."); + Debug(2, "CloseFfmpeg called."); - mCanCapture = false; + mCanCapture = false; #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &mFrame ); - av_frame_free( &mRawFrame ); + av_frame_free( &mFrame ); + av_frame_free( &mRawFrame ); #else - av_freep( &mFrame ); - av_freep( &mRawFrame ); + av_freep( &mFrame ); + av_freep( &mRawFrame ); #endif - + #if HAVE_LIBSWSCALE - if ( mConvertContext ) - { - sws_freeContext( mConvertContext ); - mConvertContext = NULL; - } + if ( mConvertContext ) + { + sws_freeContext( mConvertContext ); + mConvertContext = NULL; + } #endif - if ( mCodecContext ) - { - avcodec_close( mCodecContext ); - mCodecContext = NULL; // Freed by av_close_input_file - } - if ( mFormatContext ) - { + if ( mCodecContext ) + { + avcodec_close( mCodecContext ); + mCodecContext = NULL; // Freed by av_close_input_file + } + if ( mFormatContext ) + { #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) - av_close_input_file( mFormatContext ); + av_close_input_file( mFormatContext ); #else - avformat_close_input( &mFormatContext ); + avformat_close_input( &mFormatContext ); #endif - mFormatContext = NULL; - } + mFormatContext = NULL; + } - return 0; + return 0; } int FfmpegCamera::FfmpegInterruptCallback(void *ctx) { - FfmpegCamera* camera = reinterpret_cast(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 ); - return 1; + FfmpegCamera* camera = reinterpret_cast(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 ); + return 1; + } } - } - return 0; + return 0; } void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){ - if (ctx == NULL) return NULL; + if (ctx == NULL) return NULL; - FfmpegCamera* camera = reinterpret_cast(ctx); + FfmpegCamera* camera = reinterpret_cast(ctx); - while (1){ - // Close current stream. - camera->CloseFfmpeg(); + while (1){ + // Close current stream. + camera->CloseFfmpeg(); - // Sleep if necessary to not reconnect too fast. - int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); - wait = wait < 0 ? 0 : wait; - if (wait > 0){ - Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); - sleep(wait); + // Sleep if necessary to not reconnect too fast. + int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); + wait = wait < 0 ? 0 : wait; + if (wait > 0){ + Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); + sleep(wait); + } + + if (camera->OpenFfmpeg() == 0){ + return NULL; + } + } +} + +//Function to handle capture and store +int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_file ) +{ + if (!mCanCapture){ + return -1; + } + + // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. + if (mReopenThread != 0) { + void *retval = 0; + int ret; + + ret = pthread_join(mReopenThread, &retval); + if (ret != 0){ + Error("Could not join reopen thread."); + } + + Info( "Successfully reopened stream." ); + mReopenThread = 0; } - if (camera->OpenFfmpeg() == 0){ - return NULL; + AVPacket packet; + 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."); + return (-1); + } + + if ( mCodecContext->codec_id != AV_CODEC_ID_H264 ) { + Error( "Input stream is not h264. The stored event file may not be viewable in browser." ); } - } + + int frameComplete = false; + while ( !frameComplete ) { + int avResult = av_read_frame( mFormatContext, &packet ); + if ( avResult < 0 ) { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); + if ( + // Check if EOF. + (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || + // Check for Connection failure. + (avResult == -110) + ) { + Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); + ReopenFfmpeg(); + } + + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); + return( -1 ); + } + Debug( 5, "Got packet from stream %d", packet.stream_index ); + if ( packet.stream_index == mVideoStreamId ) { +#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) + if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) +#else + if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) +#endif + Fatal( "Unable to decode frame at frame %d", frameCount ); + + Debug( 4, "Decoded video packet at frame %d", frameCount ); + + if ( frameComplete ) { + Debug( 4, "Got frame %d", frameCount ); + + avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); + + //Keep the last keyframe so we can establish immediate video + if(packet.flags & AV_PKT_FLAG_KEY) { + //Debug(4, "Have keyframe"); + //av_copy_packet(&lastKeyframePkt, &packet); + //TODO I think we need to store the key frame location for seeking as part of the event + } + + //Video recording + if ( recording && !wasRecording ) { + //Instantiate the video storage module + Debug(3, "recording and ! wasRecording %s", event_file); + + videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime); + wasRecording = true; + strcpy(oldDirectory, event_file); + + + // Need to write out all the frames from the last keyframe? + + } else if ( ( ! recording ) && wasRecording && videoStore ) { + Info("Deleting videoStore instance"); + delete videoStore; + videoStore = NULL; + } + + // The directory we are recording to is no longer tied to the current event. + // Need to re-init the videostore with the correct directory and start recording again + // for efficiency's sake, we should test for keyframe before we test for directory change... + if ( recording && wasRecording && (packet.flags & AV_PKT_FLAG_KEY) && (strcmp(oldDirectory, event_file) != 0 ) ) { + // don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?... + // if we store our key frame location with the event will that be enough? + Info("Re-starting video storage module"); + if(videoStore){ + delete videoStore; + videoStore = NULL; + } + + videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime); + strcpy(oldDirectory, event_file); + } + + if ( videoStore && recording ) { + //Write the packet to our video store + int ret = videoStore->writeVideoFramePacket(&packet, mFormatContext->streams[mVideoStreamId]);//, &lastKeyframePkt); + if ( ret < 0 ) { //Less than zero and we skipped a frame + av_free_packet( &packet ); + return 0; + } + } + +#if HAVE_LIBSWSCALE + if ( mConvertContext == NULL ) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + if ( mConvertContext == NULL ) + Fatal( "Unable to create conversion context for %s", mPath.c_str() ); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); +#else // HAVE_LIBSWSCALE + Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); +#endif // HAVE_LIBSWSCALE + + 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 + Debug( 4, "Audio stream index %d", packet.stream_index ); + if ( frameComplete ) { + Debug( 3, "Got audio frame with framecomplete %d", frameCount ); + //} else { + //Debug( 3, "Got audio frame %d without frameComplete", frameCount ); + } + if ( videoStore && recording ) { + if ( record_audio ) { + Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index ); + //Write the packet to our video store + //FIXME no relevance of last key frame + int ret = videoStore->writeAudioFramePacket( &packet, mFormatContext->streams[packet.stream_index] ); + if ( ret < 0 ) {//Less than zero and we skipped a frame + av_free_packet( &packet ); + return 0; + } + } else { + Debug(4, "Not recording audio packet" ); + } + } + } else { +#if LIBAVUTIL_VERSION_CHECK(54, 23, 0, 23, 0) + Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codec->codec_type) ); +#else + Debug( 3, "Some other stream index %d", packet.stream_index ); +#endif + } + av_free_packet( &packet ); + } // end while ! frameComplete + return (frameCount); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index a8ed2a6a6..314ce6fa1 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -25,6 +25,7 @@ #include "zm_buffer.h" //#include "zm_utils.h" #include "zm_ffmpeg.h" +#include "zm_videostore.h" // // Class representing 'ffmpeg' cameras, i.e. those which are @@ -33,51 +34,62 @@ class FfmpegCamera : public Camera { protected: - std::string mPath; - std::string mMethod; - std::string mOptions; + std::string mPath; + std::string mMethod; + std::string mOptions; - int frameCount; + int frameCount; #if HAVE_LIBAVFORMAT - AVFormatContext *mFormatContext; - int mVideoStreamId; - AVCodecContext *mCodecContext; - AVCodec *mCodec; - AVFrame *mRawFrame; - AVFrame *mFrame; - _AVPIXELFORMAT imagePixFormat; + AVFormatContext *mFormatContext; + int mVideoStreamId; + int mAudioStreamId; + AVCodecContext *mCodecContext; + AVCodec *mCodec; + AVFrame *mRawFrame; + AVFrame *mFrame; + _AVPIXELFORMAT imagePixFormat; - int OpenFfmpeg(); - int ReopenFfmpeg(); - int CloseFfmpeg(); - static int FfmpegInterruptCallback(void *ctx); - static void* ReopenFfmpegThreadCallback(void *ctx); - bool mIsOpening; - bool mCanCapture; - int mOpenStart; - pthread_t mReopenThread; + int OpenFfmpeg(); + int ReopenFfmpeg(); + int CloseFfmpeg(); + static int FfmpegInterruptCallback(void *ctx); + static void* ReopenFfmpegThreadCallback(void *ctx); + bool mIsOpening; + bool mCanCapture; + int mOpenStart; + pthread_t mReopenThread; #endif // HAVE_LIBAVFORMAT + + bool wasRecording; + VideoStore *videoStore; + char oldDirectory[4096]; + + // Last Key frame + AVPacket lastKeyframePkt; #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext; + struct SwsContext *mConvertContext; #endif + int64_t startTime; + public: - FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~FfmpegCamera(); + FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~FfmpegCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Options() const { return( mOptions ); } - const std::string &Method() const { return( mMethod ); } + const std::string &Path() const { return( mPath ); } + const std::string &Options() const { return( mOptions ); } + const std::string &Method() const { return( mMethod ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ); + int PostCapture(); }; #endif // ZM_FFMPEG_CAMERA_H diff --git a/src/zm_file_camera.cpp b/src/zm_file_camera.cpp index 4292cea48..f9ec73b26 100644 --- a/src/zm_file_camera.cpp +++ b/src/zm_file_camera.cpp @@ -34,30 +34,30 @@ #include "zm.h" #include "zm_file_camera.h" -FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ) +FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ) { - strncpy( path, p_path, sizeof(path) ); - if ( capture ) - { - Initialise(); - } + strncpy( path, p_path, sizeof(path) ); + if ( capture ) + { + Initialise(); + } } FileCamera::~FileCamera() { - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void FileCamera::Initialise() { - if ( !path[0] ) - { - Error( "No path specified for file image" ); - exit( -1 ); - } + if ( !path[0] ) + { + Error( "No path specified for file image" ); + exit( -1 ); + } } void FileCamera::Terminate() @@ -66,26 +66,26 @@ void FileCamera::Terminate() int FileCamera::PreCapture() { - struct stat statbuf; - if ( stat( path, &statbuf ) < 0 ) - { - Error( "Can't stat %s: %s", path, strerror(errno) ); - return( -1 ); - } + struct stat statbuf; + if ( stat( path, &statbuf ) < 0 ) + { + Error( "Can't stat %s: %s", path, strerror(errno) ); + return( -1 ); + } - while ( (time( 0 ) - statbuf.st_mtime) < 1 ) - { - usleep( 100000 ); - } - return( 0 ); + while ( (time( 0 ) - statbuf.st_mtime) < 1 ) + { + usleep( 100000 ); + } + return( 0 ); } int FileCamera::Capture( Image &image ) { - return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); + return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); } int FileCamera::PostCapture() { - return( 0 ); + return( 0 ); } diff --git a/src/zm_file_camera.h b/src/zm_file_camera.h index 19e5971b8..a2ed23077 100644 --- a/src/zm_file_camera.h +++ b/src/zm_file_camera.h @@ -33,19 +33,20 @@ class FileCamera : public Camera { protected: - char path[PATH_MAX]; + char path[PATH_MAX]; public: - FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~FileCamera(); + FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~FileCamera(); - const char *Path() const { return( path ); } + const char *Path() const { return( path ); } - void Initialise(); - void Terminate(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + void Initialise(); + void Terminate(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);}; }; #endif // ZM_FILE_CAMERA_H diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index 26c4701fb..affd91011 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -25,102 +25,102 @@ // Do all the buffer checking work here to avoid unnecessary locking void* LibvlcLockBuffer(void* opaque, void** planes) { - LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; - data->mutex.lock(); - - uint8_t* buffer = data->buffer; - data->buffer = data->prevBuffer; - data->prevBuffer = buffer; - - *planes = data->buffer; - return NULL; + LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; + data->mutex.lock(); + + uint8_t* buffer = data->buffer; + data->buffer = data->prevBuffer; + data->prevBuffer = buffer; + + *planes = data->buffer; + return NULL; } void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes) { - LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; - - bool newFrame = false; - for(uint32_t i = 0; i < data->bufferSize; i++) - { - if(data->buffer[i] != data->prevBuffer[i]) + LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; + + bool newFrame = false; + for(uint32_t i = 0; i < data->bufferSize; i++) { - newFrame = true; - break; + if(data->buffer[i] != data->prevBuffer[i]) + { + newFrame = true; + break; + } + } + data->mutex.unlock(); + + time_t now; + time(&now); + // Return frames slightly faster than 1fps (if time() supports greater than one second resolution) + if(newFrame || difftime(now, data->prevTime) >= 0.8) + { + data->prevTime = now; + data->newImage.updateValueSignal(true); } - } - data->mutex.unlock(); - - time_t now; - time(&now); - // Return frames slightly faster than 1fps (if time() supports greater than one second resolution) - if(newFrame || difftime(now, data->prevTime) >= 0.8) - { - data->prevTime = now; - data->newImage.updateValueSignal(true); - } } -LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), - mMethod( p_method ), - mOptions( p_options ) -{ - mLibvlcInstance = NULL; - mLibvlcMedia = NULL; - mLibvlcMediaPlayer = NULL; - mLibvlcData.buffer = NULL; - mLibvlcData.prevBuffer = NULL; +LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + mPath( p_path ), + mMethod( p_method ), + mOptions( p_options ) +{ + mLibvlcInstance = NULL; + mLibvlcMedia = NULL; + mLibvlcMediaPlayer = NULL; + mLibvlcData.buffer = NULL; + mLibvlcData.prevBuffer = NULL; - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - mTargetChroma = "RV32"; - mBpp = 4; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_BGR; - mTargetChroma = "RV24"; - mBpp = 3; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - mTargetChroma = "GREY"; - mBpp = 1; - } else { - Panic("Unexpected colours: %d",colours); - } - - if ( capture ) - { - Initialise(); - } + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + mTargetChroma = "RV32"; + mBpp = 4; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_BGR; + mTargetChroma = "RV24"; + mBpp = 3; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + mTargetChroma = "GREY"; + mBpp = 1; + } else { + Panic("Unexpected colours: %d",colours); + } + + if ( capture ) + { + Initialise(); + } } LibvlcCamera::~LibvlcCamera() { - if ( capture ) - { - Terminate(); - } - if(mLibvlcMediaPlayer != NULL) - { - libvlc_media_player_release(mLibvlcMediaPlayer); - mLibvlcMediaPlayer = NULL; - } - if(mLibvlcMedia != NULL) - { - libvlc_media_release(mLibvlcMedia); - mLibvlcMedia = NULL; - } - if(mLibvlcInstance != NULL) - { - libvlc_release(mLibvlcInstance); - mLibvlcInstance = NULL; - } - if (mOptArgV != NULL) - { - delete[] mOptArgV; - } + if ( capture ) + { + Terminate(); + } + if(mLibvlcMediaPlayer != NULL) + { + libvlc_media_player_release(mLibvlcMediaPlayer); + mLibvlcMediaPlayer = NULL; + } + if(mLibvlcMedia != NULL) + { + libvlc_media_release(mLibvlcMedia); + mLibvlcMedia = NULL; + } + if(mLibvlcInstance != NULL) + { + libvlc_release(mLibvlcInstance); + mLibvlcInstance = NULL; + } + if (mOptArgV != NULL) + { + delete[] mOptArgV; + } } void LibvlcCamera::Initialise() @@ -129,91 +129,105 @@ void LibvlcCamera::Initialise() void LibvlcCamera::Terminate() { - libvlc_media_player_stop(mLibvlcMediaPlayer); - if(mLibvlcData.buffer != NULL) - { - zm_freealigned(mLibvlcData.buffer); - } - if(mLibvlcData.prevBuffer != NULL) - { - zm_freealigned(mLibvlcData.prevBuffer); - } + libvlc_media_player_stop(mLibvlcMediaPlayer); + if(mLibvlcData.buffer != NULL) + { + zm_freealigned(mLibvlcData.buffer); + } + if(mLibvlcData.prevBuffer != NULL) + { + zm_freealigned(mLibvlcData.prevBuffer); + } } int LibvlcCamera::PrimeCapture() { - Info("Priming capture from %s", mPath.c_str()); - - StringVector opVect = split(Options(), ","); - - // Set transport method as specified by method field, rtpUni is default - if ( Method() == "rtpMulti" ) - opVect.push_back("--rtsp-mcast"); - else if ( Method() == "rtpRtsp" ) - opVect.push_back("--rtsp-tcp"); - else if ( Method() == "rtpRtspHttp" ) - opVect.push_back("--rtsp-http"); + Info("Priming capture from %s", mPath.c_str()); + + StringVector opVect = split(Options(), ","); + + // Set transport method as specified by method field, rtpUni is default + if ( Method() == "rtpMulti" ) + opVect.push_back("--rtsp-mcast"); + else if ( Method() == "rtpRtsp" ) + opVect.push_back("--rtsp-tcp"); + else if ( Method() == "rtpRtspHttp" ) + opVect.push_back("--rtsp-http"); - if (opVect.size() > 0) - { - mOptArgV = new char*[opVect.size()]; - Debug(2, "Number of Options: %d",opVect.size()); - for (size_t i=0; i< opVect.size(); i++) { - opVect[i] = trimSpaces(opVect[i]); - mOptArgV[i] = (char *)opVect[i].c_str(); - Debug(2, "set option %d to '%s'", i, opVect[i].c_str()); + if (opVect.size() > 0) + { + mOptArgV = new char*[opVect.size()]; + Debug(2, "Number of Options: %d",opVect.size()); + for (size_t i=0; i< opVect.size(); i++) { + opVect[i] = trimSpaces(opVect[i]); + mOptArgV[i] = (char *)opVect[i].c_str(); + Debug(2, "set option %d to '%s'", i, opVect[i].c_str()); + } } - } - mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV); - if(mLibvlcInstance == NULL) - Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg()); - - mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); - if(mLibvlcMedia == NULL) - Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); - - mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); - if(mLibvlcMediaPlayer == NULL) - Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); + mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV); + if(mLibvlcInstance == NULL) + Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg()); + + mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); + if(mLibvlcMedia == NULL) + Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); + + mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); + if(mLibvlcMediaPlayer == NULL) + Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); - libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); - libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); + libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); + libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); - mLibvlcData.bufferSize = width * height * mBpp; - // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) - mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); - mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); - - mLibvlcData.newImage.setValueImmediate(false); + mLibvlcData.bufferSize = width * height * mBpp; + // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) + mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); + mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); + + mLibvlcData.newImage.setValueImmediate(false); - libvlc_media_player_play(mLibvlcMediaPlayer); - - return(0); + libvlc_media_player_play(mLibvlcMediaPlayer); + + return(0); } int LibvlcCamera::PreCapture() -{ - return(0); +{ + return(0); } // Should not return -1 as cancels capture. Always wait for image if available. int LibvlcCamera::Capture( Image &image ) { - while(!mLibvlcData.newImage.getValueImmediate()) - mLibvlcData.newImage.getUpdatedValue(1); + while(!mLibvlcData.newImage.getValueImmediate()) + mLibvlcData.newImage.getUpdatedValue(1); - mLibvlcData.mutex.lock(); - image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); - mLibvlcData.newImage.setValueImmediate(false); - mLibvlcData.mutex.unlock(); - - return (0); + mLibvlcData.mutex.lock(); + image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); + mLibvlcData.newImage.setValueImmediate(false); + mLibvlcData.mutex.unlock(); + + return (0); +} + +// Should not return -1 as cancels capture. Always wait for image if available. +int LibvlcCamera::CaptureAndRecord( Image &image, bool recording, char* event_directory ) +{ + while(!mLibvlcData.newImage.getValueImmediate()) + mLibvlcData.newImage.getUpdatedValue(1); + + mLibvlcData.mutex.lock(); + image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); + mLibvlcData.newImage.setValueImmediate(false); + mLibvlcData.mutex.unlock(); + + return (0); } int LibvlcCamera::PostCapture() { - return(0); + return(0); } #endif // HAVE_LIBVLC diff --git a/src/zm_libvlc_camera.h b/src/zm_libvlc_camera.h index d08b310f5..8c466b5ae 100644 --- a/src/zm_libvlc_camera.h +++ b/src/zm_libvlc_camera.h @@ -33,44 +33,45 @@ // Used by libvlc callbacks struct LibvlcPrivateData { - uint8_t* buffer; - uint8_t* prevBuffer; - time_t prevTime; - uint32_t bufferSize; - Mutex mutex; - ThreadData newImage; + uint8_t* buffer; + uint8_t* prevBuffer; + time_t prevTime; + uint32_t bufferSize; + Mutex mutex; + ThreadData newImage; }; class LibvlcCamera : public Camera { protected: - std::string mPath; - std::string mMethod; - std::string mOptions; - char **mOptArgV; - LibvlcPrivateData mLibvlcData; - std::string mTargetChroma; - uint8_t mBpp; + std::string mPath; + std::string mMethod; + std::string mOptions; + char **mOptArgV; + LibvlcPrivateData mLibvlcData; + std::string mTargetChroma; + uint8_t mBpp; - libvlc_instance_t *mLibvlcInstance; - libvlc_media_t *mLibvlcMedia; - libvlc_media_player_t *mLibvlcMediaPlayer; + libvlc_instance_t *mLibvlcInstance; + libvlc_media_t *mLibvlcMedia; + libvlc_media_player_t *mLibvlcMediaPlayer; public: - LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~LibvlcCamera(); + LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~LibvlcCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Options() const { return( mOptions ); } - const std::string &Method() const { return( mMethod ); } + const std::string &Path() const { return( mPath ); } + const std::string &Options() const { return( mOptions ); } + const std::string &Method() const { return( mMethod ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ); + int PostCapture(); }; #endif // HAVE_LIBVLC diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index 6e1a21d6e..ca5f79472 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -43,216 +43,216 @@ static unsigned int BigEndian; static int vidioctl( int fd, int request, void *arg ) { - int result = -1; - do - { - result = ioctl( fd, request, arg ); - } while ( result == -1 && errno == EINTR ); - return( result ); + int result = -1; + do + { + result = ioctl( fd, request, arg ); + } while ( result == -1 && errno == EINTR ); + return( result ); } #if HAVE_LIBSWSCALE static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette ) { - _AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE; - + _AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE; + #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - switch( palette ) + if ( v4l_version == 2 ) { + switch( palette ) + { #if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444) - case V4L2_PIX_FMT_RGB444 : - pixFormat = AV_PIX_FMT_RGB444; - break; + case V4L2_PIX_FMT_RGB444 : + pixFormat = AV_PIX_FMT_RGB444; + break; #endif // V4L2_PIX_FMT_RGB444 - case V4L2_PIX_FMT_RGB555 : - pixFormat = AV_PIX_FMT_RGB555; - break; - case V4L2_PIX_FMT_RGB565 : - pixFormat = AV_PIX_FMT_RGB565; - break; - case V4L2_PIX_FMT_BGR24 : - pixFormat = AV_PIX_FMT_BGR24; - break; - case V4L2_PIX_FMT_RGB24 : - pixFormat = AV_PIX_FMT_RGB24; - break; - case V4L2_PIX_FMT_BGR32 : - pixFormat = AV_PIX_FMT_BGRA; - break; - case V4L2_PIX_FMT_RGB32 : - pixFormat = AV_PIX_FMT_ARGB; - break; - case V4L2_PIX_FMT_GREY : - pixFormat = AV_PIX_FMT_GRAY8; - break; - case V4L2_PIX_FMT_YUYV : - pixFormat = AV_PIX_FMT_YUYV422; - break; - case V4L2_PIX_FMT_YUV422P : - pixFormat = AV_PIX_FMT_YUV422P; - break; - case V4L2_PIX_FMT_YUV411P : - pixFormat = AV_PIX_FMT_YUV411P; - break; + case V4L2_PIX_FMT_RGB555 : + pixFormat = AV_PIX_FMT_RGB555; + break; + case V4L2_PIX_FMT_RGB565 : + pixFormat = AV_PIX_FMT_RGB565; + break; + case V4L2_PIX_FMT_BGR24 : + pixFormat = AV_PIX_FMT_BGR24; + break; + case V4L2_PIX_FMT_RGB24 : + pixFormat = AV_PIX_FMT_RGB24; + break; + case V4L2_PIX_FMT_BGR32 : + pixFormat = AV_PIX_FMT_BGRA; + break; + case V4L2_PIX_FMT_RGB32 : + pixFormat = AV_PIX_FMT_ARGB; + break; + case V4L2_PIX_FMT_GREY : + pixFormat = AV_PIX_FMT_GRAY8; + break; + case V4L2_PIX_FMT_YUYV : + pixFormat = AV_PIX_FMT_YUYV422; + break; + case V4L2_PIX_FMT_YUV422P : + pixFormat = AV_PIX_FMT_YUV422P; + break; + case V4L2_PIX_FMT_YUV411P : + pixFormat = AV_PIX_FMT_YUV411P; + break; #ifdef V4L2_PIX_FMT_YUV444 - case V4L2_PIX_FMT_YUV444 : - pixFormat = AV_PIX_FMT_YUV444P; - break; + case V4L2_PIX_FMT_YUV444 : + pixFormat = AV_PIX_FMT_YUV444P; + break; #endif // V4L2_PIX_FMT_YUV444 - case V4L2_PIX_FMT_YUV410 : - pixFormat = AV_PIX_FMT_YUV410P; - break; - case V4L2_PIX_FMT_YUV420 : - pixFormat = AV_PIX_FMT_YUV420P; - break; - case V4L2_PIX_FMT_JPEG : - case V4L2_PIX_FMT_MJPEG : - pixFormat = AV_PIX_FMT_YUVJ444P; - break; - case V4L2_PIX_FMT_UYVY : - pixFormat = AV_PIX_FMT_UYVY422; - break; - // These don't seem to have ffmpeg equivalents - // See if you can match any of the ones in the default clause below!? - case V4L2_PIX_FMT_RGB332 : - case V4L2_PIX_FMT_RGB555X : - case V4L2_PIX_FMT_RGB565X : - //case V4L2_PIX_FMT_Y16 : - //case V4L2_PIX_FMT_PAL8 : - case V4L2_PIX_FMT_YVU410 : - case V4L2_PIX_FMT_YVU420 : - case V4L2_PIX_FMT_Y41P : - //case V4L2_PIX_FMT_YUV555 : - //case V4L2_PIX_FMT_YUV565 : - //case V4L2_PIX_FMT_YUV32 : - case V4L2_PIX_FMT_NV12 : - case V4L2_PIX_FMT_NV21 : - case V4L2_PIX_FMT_YYUV : - case V4L2_PIX_FMT_HI240 : - case V4L2_PIX_FMT_HM12 : - //case V4L2_PIX_FMT_SBGGR8 : - //case V4L2_PIX_FMT_SGBRG8 : - //case V4L2_PIX_FMT_SBGGR16 : - case V4L2_PIX_FMT_DV : - case V4L2_PIX_FMT_MPEG : - case V4L2_PIX_FMT_WNVA : - case V4L2_PIX_FMT_SN9C10X : - case V4L2_PIX_FMT_PWC1 : - case V4L2_PIX_FMT_PWC2 : - case V4L2_PIX_FMT_ET61X251 : - //case V4L2_PIX_FMT_SPCA501 : - //case V4L2_PIX_FMT_SPCA505 : - //case V4L2_PIX_FMT_SPCA508 : - //case V4L2_PIX_FMT_SPCA561 : - //case V4L2_PIX_FMT_PAC207 : - //case V4L2_PIX_FMT_PJPG : - //case V4L2_PIX_FMT_YVYU : - default : - { - Fatal( "Can't find swscale format for palette %d", palette ); - break; - // These are all spare and may match some of the above - pixFormat = AV_PIX_FMT_YUVJ420P; - pixFormat = AV_PIX_FMT_YUVJ422P; - pixFormat = AV_PIX_FMT_UYVY422; - pixFormat = AV_PIX_FMT_UYYVYY411; - pixFormat = AV_PIX_FMT_BGR565; - pixFormat = AV_PIX_FMT_BGR555; - pixFormat = AV_PIX_FMT_BGR8; - pixFormat = AV_PIX_FMT_BGR4; - pixFormat = AV_PIX_FMT_BGR4_BYTE; - pixFormat = AV_PIX_FMT_RGB8; - pixFormat = AV_PIX_FMT_RGB4; - pixFormat = AV_PIX_FMT_RGB4_BYTE; - pixFormat = AV_PIX_FMT_NV12; - pixFormat = AV_PIX_FMT_NV21; - pixFormat = AV_PIX_FMT_RGB32_1; - pixFormat = AV_PIX_FMT_BGR32_1; - pixFormat = AV_PIX_FMT_GRAY16BE; - pixFormat = AV_PIX_FMT_GRAY16LE; - pixFormat = AV_PIX_FMT_YUV440P; - pixFormat = AV_PIX_FMT_YUVJ440P; - pixFormat = AV_PIX_FMT_YUVA420P; - //pixFormat = AV_PIX_FMT_VDPAU_H264; - //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; - //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; - } + case V4L2_PIX_FMT_YUV410 : + pixFormat = AV_PIX_FMT_YUV410P; + break; + case V4L2_PIX_FMT_YUV420 : + pixFormat = AV_PIX_FMT_YUV420P; + break; + case V4L2_PIX_FMT_JPEG : + case V4L2_PIX_FMT_MJPEG : + pixFormat = AV_PIX_FMT_YUVJ444P; + break; + case V4L2_PIX_FMT_UYVY : + pixFormat = AV_PIX_FMT_UYVY422; + break; + // These don't seem to have ffmpeg equivalents + // See if you can match any of the ones in the default clause below!? + case V4L2_PIX_FMT_RGB332 : + case V4L2_PIX_FMT_RGB555X : + case V4L2_PIX_FMT_RGB565X : + //case V4L2_PIX_FMT_Y16 : + //case V4L2_PIX_FMT_PAL8 : + case V4L2_PIX_FMT_YVU410 : + case V4L2_PIX_FMT_YVU420 : + case V4L2_PIX_FMT_Y41P : + //case V4L2_PIX_FMT_YUV555 : + //case V4L2_PIX_FMT_YUV565 : + //case V4L2_PIX_FMT_YUV32 : + case V4L2_PIX_FMT_NV12 : + case V4L2_PIX_FMT_NV21 : + case V4L2_PIX_FMT_YYUV : + case V4L2_PIX_FMT_HI240 : + case V4L2_PIX_FMT_HM12 : + //case V4L2_PIX_FMT_SBGGR8 : + //case V4L2_PIX_FMT_SGBRG8 : + //case V4L2_PIX_FMT_SBGGR16 : + case V4L2_PIX_FMT_DV : + case V4L2_PIX_FMT_MPEG : + case V4L2_PIX_FMT_WNVA : + case V4L2_PIX_FMT_SN9C10X : + case V4L2_PIX_FMT_PWC1 : + case V4L2_PIX_FMT_PWC2 : + case V4L2_PIX_FMT_ET61X251 : + //case V4L2_PIX_FMT_SPCA501 : + //case V4L2_PIX_FMT_SPCA505 : + //case V4L2_PIX_FMT_SPCA508 : + //case V4L2_PIX_FMT_SPCA561 : + //case V4L2_PIX_FMT_PAC207 : + //case V4L2_PIX_FMT_PJPG : + //case V4L2_PIX_FMT_YVYU : + default : + { + Fatal( "Can't find swscale format for palette %d", palette ); + break; + // These are all spare and may match some of the above + pixFormat = AV_PIX_FMT_YUVJ420P; + pixFormat = AV_PIX_FMT_YUVJ422P; + pixFormat = AV_PIX_FMT_UYVY422; + pixFormat = AV_PIX_FMT_UYYVYY411; + pixFormat = AV_PIX_FMT_BGR565; + pixFormat = AV_PIX_FMT_BGR555; + pixFormat = AV_PIX_FMT_BGR8; + pixFormat = AV_PIX_FMT_BGR4; + pixFormat = AV_PIX_FMT_BGR4_BYTE; + pixFormat = AV_PIX_FMT_RGB8; + pixFormat = AV_PIX_FMT_RGB4; + pixFormat = AV_PIX_FMT_RGB4_BYTE; + pixFormat = AV_PIX_FMT_NV12; + pixFormat = AV_PIX_FMT_NV21; + pixFormat = AV_PIX_FMT_RGB32_1; + pixFormat = AV_PIX_FMT_BGR32_1; + pixFormat = AV_PIX_FMT_GRAY16BE; + pixFormat = AV_PIX_FMT_GRAY16LE; + pixFormat = AV_PIX_FMT_YUV440P; + pixFormat = AV_PIX_FMT_YUVJ440P; + pixFormat = AV_PIX_FMT_YUVA420P; + //pixFormat = AV_PIX_FMT_VDPAU_H264; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; + } + } } - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - switch( palette ) + if ( v4l_version == 1 ) { - case VIDEO_PALETTE_RGB32 : - if(BigEndian) - pixFormat = AV_PIX_FMT_ARGB; - else - pixFormat = AV_PIX_FMT_BGRA; - break; - case VIDEO_PALETTE_RGB24 : - if(BigEndian) - pixFormat = AV_PIX_FMT_RGB24; - else - pixFormat = AV_PIX_FMT_BGR24; - break; - case VIDEO_PALETTE_GREY : - pixFormat = AV_PIX_FMT_GRAY8; - break; - case VIDEO_PALETTE_RGB555 : - pixFormat = AV_PIX_FMT_RGB555; - break; - case VIDEO_PALETTE_RGB565 : - pixFormat = AV_PIX_FMT_RGB565; - break; - case VIDEO_PALETTE_YUYV : - case VIDEO_PALETTE_YUV422 : - pixFormat = AV_PIX_FMT_YUYV422; - break; - case VIDEO_PALETTE_YUV422P : - pixFormat = AV_PIX_FMT_YUV422P; - break; - case VIDEO_PALETTE_YUV420P : - pixFormat = AV_PIX_FMT_YUV420P; - break; - default : - { - Fatal( "Can't find swscale format for palette %d", palette ); - break; - // These are all spare and may match some of the above - pixFormat = AV_PIX_FMT_YUVJ420P; - pixFormat = AV_PIX_FMT_YUVJ422P; - pixFormat = AV_PIX_FMT_YUVJ444P; - pixFormat = AV_PIX_FMT_UYVY422; - pixFormat = AV_PIX_FMT_UYYVYY411; - pixFormat = AV_PIX_FMT_BGR565; - pixFormat = AV_PIX_FMT_BGR555; - pixFormat = AV_PIX_FMT_BGR8; - pixFormat = AV_PIX_FMT_BGR4; - pixFormat = AV_PIX_FMT_BGR4_BYTE; - pixFormat = AV_PIX_FMT_RGB8; - pixFormat = AV_PIX_FMT_RGB4; - pixFormat = AV_PIX_FMT_RGB4_BYTE; - pixFormat = AV_PIX_FMT_NV12; - pixFormat = AV_PIX_FMT_NV21; - pixFormat = AV_PIX_FMT_RGB32_1; - pixFormat = AV_PIX_FMT_BGR32_1; - pixFormat = AV_PIX_FMT_GRAY16BE; - pixFormat = AV_PIX_FMT_GRAY16LE; - pixFormat = AV_PIX_FMT_YUV440P; - pixFormat = AV_PIX_FMT_YUVJ440P; - pixFormat = AV_PIX_FMT_YUVA420P; - //pixFormat = AV_PIX_FMT_VDPAU_H264; - //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; - //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; - } + switch( palette ) + { + case VIDEO_PALETTE_RGB32 : + if(BigEndian) + pixFormat = AV_PIX_FMT_ARGB; + else + pixFormat = AV_PIX_FMT_BGRA; + break; + case VIDEO_PALETTE_RGB24 : + if(BigEndian) + pixFormat = AV_PIX_FMT_RGB24; + else + pixFormat = AV_PIX_FMT_BGR24; + break; + case VIDEO_PALETTE_GREY : + pixFormat = AV_PIX_FMT_GRAY8; + break; + case VIDEO_PALETTE_RGB555 : + pixFormat = AV_PIX_FMT_RGB555; + break; + case VIDEO_PALETTE_RGB565 : + pixFormat = AV_PIX_FMT_RGB565; + break; + case VIDEO_PALETTE_YUYV : + case VIDEO_PALETTE_YUV422 : + pixFormat = AV_PIX_FMT_YUYV422; + break; + case VIDEO_PALETTE_YUV422P : + pixFormat = AV_PIX_FMT_YUV422P; + break; + case VIDEO_PALETTE_YUV420P : + pixFormat = AV_PIX_FMT_YUV420P; + break; + default : + { + Fatal( "Can't find swscale format for palette %d", palette ); + break; + // These are all spare and may match some of the above + pixFormat = AV_PIX_FMT_YUVJ420P; + pixFormat = AV_PIX_FMT_YUVJ422P; + pixFormat = AV_PIX_FMT_YUVJ444P; + pixFormat = AV_PIX_FMT_UYVY422; + pixFormat = AV_PIX_FMT_UYYVYY411; + pixFormat = AV_PIX_FMT_BGR565; + pixFormat = AV_PIX_FMT_BGR555; + pixFormat = AV_PIX_FMT_BGR8; + pixFormat = AV_PIX_FMT_BGR4; + pixFormat = AV_PIX_FMT_BGR4_BYTE; + pixFormat = AV_PIX_FMT_RGB8; + pixFormat = AV_PIX_FMT_RGB4; + pixFormat = AV_PIX_FMT_RGB4_BYTE; + pixFormat = AV_PIX_FMT_NV12; + pixFormat = AV_PIX_FMT_NV21; + pixFormat = AV_PIX_FMT_RGB32_1; + pixFormat = AV_PIX_FMT_BGR32_1; + pixFormat = AV_PIX_FMT_GRAY16BE; + pixFormat = AV_PIX_FMT_GRAY16LE; + pixFormat = AV_PIX_FMT_YUV440P; + pixFormat = AV_PIX_FMT_YUVJ440P; + pixFormat = AV_PIX_FMT_YUVA420P; + //pixFormat = AV_PIX_FMT_VDPAU_H264; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; + } + } } - } #endif // ZM_HAS_V4L1 - return( pixFormat ); + return( pixFormat ); } #endif // HAVE_LIBSWSCALE @@ -286,1936 +286,1985 @@ AVFrame **LocalCamera::capturePictures = 0; LocalCamera *LocalCamera::last_camera = NULL; -LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, int p_standard, bool p_v4l_multi_buffer, unsigned int p_v4l_captures_per_frame, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras) : - Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - device( p_device ), - channel( p_channel ), - standard( p_standard ), - palette( p_palette ), - channel_index( 0 ), - extras ( p_extras ) +LocalCamera::LocalCamera( + int p_id, + const std::string &p_device, + int p_channel, + int p_standard, + bool p_v4l_multi_buffer, + unsigned int p_v4l_captures_per_frame, + const std::string &p_method, + int p_width, + int p_height, + int p_colours, + int p_palette, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio, + unsigned int p_extras) : + Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + device( p_device ), + channel( p_channel ), + standard( p_standard ), + palette( p_palette ), + channel_index( 0 ), + extras ( p_extras ) { - // If we are the first, or only, input on this device then - // do the initial opening etc - device_prime = (camera_count++ == 0); - v4l_version = (p_method=="v4l2"?2:1); - v4l_multi_buffer = p_v4l_multi_buffer; - v4l_captures_per_frame = p_v4l_captures_per_frame; - - if ( capture ) - { - if ( device_prime ) + // If we are the first, or only, input on this device then + // do the initial opening etc + device_prime = (camera_count++ == 0); + v4l_version = (p_method=="v4l2"?2:1); + v4l_multi_buffer = p_v4l_multi_buffer; + v4l_captures_per_frame = p_v4l_captures_per_frame; + + if ( capture ) { - Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); - } + if ( device_prime ) + { + Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); + } - if ( !last_camera || channel != last_camera->channel ) - { - // We are the first, or only, input that uses this channel - channel_prime = true; - channel_index = channel_count++; - channels[channel_index] = channel; - standards[channel_index] = standard; - } - else - { - // We are the second, or subsequent, input using this channel - channel_prime = false; + if ( !last_camera || channel != last_camera->channel ) + { + // We are the first, or only, input that uses this channel + channel_prime = true; + channel_index = channel_count++; + channels[channel_index] = channel; + standards[channel_index] = standard; + } + else + { + // We are the second, or subsequent, input using this channel + channel_prime = false; + } + } - } - - /* The V4L1 API doesn't care about endianness, we need to check the endianness of the machine */ - uint32_t checkval = 0xAABBCCDD; - if(*(unsigned char*)&checkval == 0xDD) { - BigEndian = 0; - Debug(2,"little-endian processor detected"); - } else if(*(unsigned char*)&checkval == 0xAA) { - BigEndian = 1; - Debug(2,"Big-endian processor detected"); - } else { - Error("Unable to detect the processor's endianness. Assuming little-endian."); - BigEndian = 0; - } - + /* The V4L1 API doesn't care about endianness, we need to check the endianness of the machine */ + uint32_t checkval = 0xAABBCCDD; + if(*(unsigned char*)&checkval == 0xDD) { + BigEndian = 0; + Debug(2,"little-endian processor detected"); + } else if(*(unsigned char*)&checkval == 0xAA) { + BigEndian = 1; + Debug(2,"Big-endian processor detected"); + } else { + Error("Unable to detect the processor's endianness. Assuming little-endian."); + BigEndian = 0; + } + #if ZM_HAS_V4L2 - if( v4l_version == 2 && palette == 0 ) { - /* Use automatic format selection */ - Debug(2,"Using automatic format selection"); - palette = AutoSelectFormat(colours); - if(palette == 0) { - Error("Automatic format selection failed. Falling back to YUYV"); - palette = V4L2_PIX_FMT_YUYV; - } else { - if(capture) { - Info("Selected capture palette: %s (%c%c%c%c)", palette_desc, palette&0xff, (palette>>8)&0xff, (palette>>16)&0xff, (palette>>24)&0xff); - } - } - } + if( v4l_version == 2 && palette == 0 ) { + /* Use automatic format selection */ + Debug(2,"Using automatic format selection"); + palette = AutoSelectFormat(colours); + if(palette == 0) { + Error("Automatic format selection failed. Falling back to YUYV"); + palette = V4L2_PIX_FMT_YUYV; + } else { + if(capture) { + Info("Selected capture palette: %s (%c%c%c%c)", palette_desc, palette&0xff, (palette>>8)&0xff, (palette>>16)&0xff, (palette>>24)&0xff); + } + } + } #endif - - if( capture ) { - if ( last_camera ) { - if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) - Fatal( "Different Video For Linux version used for monitors sharing same device" ); - - if ( standard != last_camera->standard ) - Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - - if ( palette != last_camera->palette ) - Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - - if ( width != last_camera->width || height != last_camera->height ) - Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - } - + + if( capture ) { + if ( last_camera ) { + if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) + Fatal( "Different Video For Linux version used for monitors sharing same device" ); + + if ( standard != last_camera->standard ) + Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + + if ( palette != last_camera->palette ) + Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + + if ( width != last_camera->width || height != last_camera->height ) + Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + } + #if HAVE_LIBSWSCALE - /* Get ffmpeg pixel format based on capture palette and endianness */ - capturePixFormat = getFfPixFormatFromV4lPalette( v4l_version, palette ); - imagePixFormat = AV_PIX_FMT_NONE; + /* Get ffmpeg pixel format based on capture palette and endianness */ + capturePixFormat = getFfPixFormatFromV4lPalette( v4l_version, palette ); + imagePixFormat = AV_PIX_FMT_NONE; #endif // HAVE_LIBSWSCALE - } + } - /* V4L2 format matching */ + /* V4L2 format matching */ #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) { - /* Try to find a match for the selected palette and target colourspace */ - - /* RGB32 palette and 32bit target colourspace */ - if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_ARGB; - - /* BGR32 palette and 32bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - - /* RGB24 palette and 24bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_RGB24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - - /* BGR24 palette and 24bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_BGR24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_BGR; - - /* Grayscale palette and grayscale target colourspace */ - } else if(palette == V4L2_PIX_FMT_GREY && colours == ZM_COLOUR_GRAY8) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ - } else { - if( capture ) + if ( v4l_version == 2 ) { + /* Try to find a match for the selected palette and target colourspace */ + + /* RGB32 palette and 32bit target colourspace */ + if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_ARGB; + + /* BGR32 palette and 32bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + + /* RGB24 palette and 24bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_RGB24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + + /* BGR24 palette and 24bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_BGR24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_BGR; + + /* Grayscale palette and grayscale target colourspace */ + } else if(palette == V4L2_PIX_FMT_GREY && colours == ZM_COLOUR_GRAY8) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ + } else { + if( capture ) #if HAVE_LIBSWSCALE - Info("No direct match for the selected palette (%c%c%c%c) and target colorspace (%d). Format conversion is required, performance penalty expected", (capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff), colours ); + Info("No direct match for the selected palette (%c%c%c%c) and target colorspace (%d). Format conversion is required, performance penalty expected", (capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff), colours ); #else - Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); + Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #endif #if HAVE_LIBSWSCALE - /* Try using swscale for the conversion */ - conversion_type = 1; - Debug(2,"Using swscale for image conversion"); - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = AV_PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - if( capture ) { + /* Try using swscale for the conversion */ + conversion_type = 1; + Debug(2,"Using swscale for image conversion"); + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + if( capture ) { #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) - if(!sws_isSupportedInput(capturePixFormat)) { - Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff)); - conversion_type = 2; /* Try ZM format conversions */ - } - if(!sws_isSupportedOutput(imagePixFormat)) { - Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - conversion_type = 2; /* Try ZM format conversions */ - } + if(!sws_isSupportedInput(capturePixFormat)) { + Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff)); + conversion_type = 2; /* Try ZM format conversions */ + } + if(!sws_isSupportedOutput(imagePixFormat)) { + Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + conversion_type = 2; /* Try ZM format conversions */ + } #endif - } + } #else - /* Don't have swscale, see what we can do */ - conversion_type = 2; + /* Don't have swscale, see what we can do */ + conversion_type = 2; #endif - /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ - if(colours == ZM_COLOUR_GRAY8 && palette == V4L2_PIX_FMT_YUYV) { - conversion_type = 2; - } - - /* JPEG */ - if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { - Debug(2,"Using JPEG image decoding"); - conversion_type = 3; - } - - if(conversion_type == 2) { - Debug(2,"Using ZM for image conversion"); - if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { - conversion_fptr = &std_convert_argb_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_GRAY8) { - conversion_fptr = &std_convert_bgra_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8) { - /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if(config.cpu_extensions && sseversion >= 35) { - conversion_fptr = &ssse3_convert_yuyv_gray8; - Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); - } else { - conversion_fptr = &std_convert_yuyv_gray8; - Debug(2,"Using standard YUYV->grayscale fast conversion"); - } - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_yuyv_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_yuyv_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb555_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb555_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb565_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb565_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else { - Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); - } - } - } - } + /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ + if(colours == ZM_COLOUR_GRAY8 && palette == V4L2_PIX_FMT_YUYV) { + conversion_type = 2; + } + + /* JPEG */ + if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { + Debug(2,"Using JPEG image decoding"); + conversion_type = 3; + } + + if(conversion_type == 2) { + Debug(2,"Using ZM for image conversion"); + if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { + conversion_fptr = &std_convert_argb_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_GRAY8) { + conversion_fptr = &std_convert_bgra_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8) { + /* Fast YUYV->Grayscale conversion by extracting the Y channel */ + if(config.cpu_extensions && sseversion >= 35) { + conversion_fptr = &ssse3_convert_yuyv_gray8; + Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); + } else { + conversion_fptr = &std_convert_yuyv_gray8; + Debug(2,"Using standard YUYV->grayscale fast conversion"); + } + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_yuyv_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_yuyv_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb555_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb555_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb565_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb565_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else { + Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); + } + } + } + } #endif // ZM_HAS_V4L2 - /* V4L1 format matching */ + /* V4L1 format matching */ #if ZM_HAS_V4L1 - if ( v4l_version == 1) { - /* Try to find a match for the selected palette and target colourspace */ - - /* RGB32 palette and 32bit target colourspace */ - if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - if(BigEndian) { - subpixelorder = ZM_SUBPIX_ORDER_ARGB; - } else { - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - } - - /* RGB24 palette and 24bit target colourspace */ - } else if(palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - if(BigEndian) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else { - subpixelorder = ZM_SUBPIX_ORDER_BGR; - } - - /* Grayscale palette and grayscale target colourspace */ - } else if(palette == VIDEO_PALETTE_GREY && colours == ZM_COLOUR_GRAY8) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ - } else { - if( capture ) - Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); + if ( v4l_version == 1) { + /* Try to find a match for the selected palette and target colourspace */ + + /* RGB32 palette and 32bit target colourspace */ + if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + if(BigEndian) { + subpixelorder = ZM_SUBPIX_ORDER_ARGB; + } else { + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + } + + /* RGB24 palette and 24bit target colourspace */ + } else if(palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + if(BigEndian) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else { + subpixelorder = ZM_SUBPIX_ORDER_BGR; + } + + /* Grayscale palette and grayscale target colourspace */ + } else if(palette == VIDEO_PALETTE_GREY && colours == ZM_COLOUR_GRAY8) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ + } else { + if( capture ) + Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #if HAVE_LIBSWSCALE - /* Try using swscale for the conversion */ - conversion_type = 1; - Debug(2,"Using swscale for image conversion"); - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = AV_PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - if( capture ) { - if(!sws_isSupportedInput(capturePixFormat)) { - Error("swscale does not support the used capture format"); - conversion_type = 2; /* Try ZM format conversions */ - } - if(!sws_isSupportedOutput(imagePixFormat)) { - Error("swscale does not support the target format"); - conversion_type = 2; /* Try ZM format conversions */ - } - } + /* Try using swscale for the conversion */ + conversion_type = 1; + Debug(2,"Using swscale for image conversion"); + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + if( capture ) { + if(!sws_isSupportedInput(capturePixFormat)) { + Error("swscale does not support the used capture format"); + conversion_type = 2; /* Try ZM format conversions */ + } + if(!sws_isSupportedOutput(imagePixFormat)) { + Error("swscale does not support the target format"); + conversion_type = 2; /* Try ZM format conversions */ + } + } #else - /* Don't have swscale, see what we can do */ - conversion_type = 2; + /* Don't have swscale, see what we can do */ + conversion_type = 2; #endif - /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ - if(colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422)) { - conversion_type = 2; - } - - if(conversion_type == 2) { - Debug(2,"Using ZM for image conversion"); - if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_GRAY8) { - if(BigEndian) { - conversion_fptr = &std_convert_argb_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else { - conversion_fptr = &std_convert_bgra_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8) { - /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if(config.cpu_extensions && sseversion >= 35) { - conversion_fptr = &ssse3_convert_yuyv_gray8; - Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); - } else { - conversion_fptr = &std_convert_yuyv_gray8; - Debug(2,"Using standard YUYV->grayscale fast conversion"); - } - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_yuyv_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_yuyv_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb555_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb555_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb565_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb565_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else { - Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); - } - } - } - } -#endif // ZM_HAS_V4L1 + /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ + if(colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422)) { + conversion_type = 2; + } + + if(conversion_type == 2) { + Debug(2,"Using ZM for image conversion"); + if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_GRAY8) { + if(BigEndian) { + conversion_fptr = &std_convert_argb_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else { + conversion_fptr = &std_convert_bgra_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8) { + /* Fast YUYV->Grayscale conversion by extracting the Y channel */ + if(config.cpu_extensions && sseversion >= 35) { + conversion_fptr = &ssse3_convert_yuyv_gray8; + Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); + } else { + conversion_fptr = &std_convert_yuyv_gray8; + Debug(2,"Using standard YUYV->grayscale fast conversion"); + } + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_yuyv_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_yuyv_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb555_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb555_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb565_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb565_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else { + Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); + } + } + } + } +#endif // ZM_HAS_V4L1 - last_camera = this; - Debug(3,"Selected subpixelorder: %d",subpixelorder); + last_camera = this; + Debug(3,"Selected subpixelorder: %d",subpixelorder); #if HAVE_LIBSWSCALE - /* Initialize swscale stuff */ - if(capture && conversion_type == 1) { + /* Initialize swscale stuff */ + if(capture && conversion_type == 1) { #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - tmpPicture = av_frame_alloc(); + tmpPicture = av_frame_alloc(); #else - tmpPicture = avcodec_alloc_frame(); + tmpPicture = avcodec_alloc_frame(); #endif - if ( !tmpPicture ) - Fatal( "Could not allocate temporary picture" ); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } - - imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); - - if ( !imgConversionContext ) { - Fatal( "Unable to initialise image scaling context" ); - } - - } + if ( !tmpPicture ) + Fatal( "Could not allocate temporary picture" ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } + + imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if ( !imgConversionContext ) { + Fatal( "Unable to initialise image scaling context" ); + } + + } #endif } LocalCamera::~LocalCamera() { - if ( device_prime && capture ) - Terminate(); - + if ( device_prime && capture ) + Terminate(); + #if HAVE_LIBSWSCALE - /* Clean up swscale stuff */ - if(capture && conversion_type == 1) { - sws_freeContext(imgConversionContext); - imgConversionContext = NULL; - + /* Clean up swscale stuff */ + if(capture && conversion_type == 1) { + sws_freeContext(imgConversionContext); + imgConversionContext = NULL; + #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &tmpPicture ); + av_frame_free( &tmpPicture ); #else - av_freep( &tmpPicture ); + av_freep( &tmpPicture ); #endif - } + } #endif } void LocalCamera::Initialise() { #if HAVE_LIBSWSCALE - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); #endif // HAVE_LIBSWSCALE - struct stat st; + struct stat st; - if ( stat( device.c_str(), &st ) < 0 ) - Fatal( "Failed to stat video device %s: %s", device.c_str(), strerror(errno) ); + if ( stat( device.c_str(), &st ) < 0 ) + Fatal( "Failed to stat video device %s: %s", device.c_str(), strerror(errno) ); - if ( !S_ISCHR(st.st_mode) ) - Fatal( "File %s is not device file: %s", device.c_str(), strerror(errno) ); + if ( !S_ISCHR(st.st_mode) ) + Fatal( "File %s is not device file: %s", device.c_str(), strerror(errno) ); - Debug( 3, "Opening video device %s", device.c_str() ); - //if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 ) - if ( (vid_fd = open( device.c_str(), O_RDWR, 0 )) < 0 ) - Fatal( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); + Debug( 3, "Opening video device %s", device.c_str() ); + //if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 ) + if ( (vid_fd = open( device.c_str(), O_RDWR, 0 )) < 0 ) + Fatal( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); #if ZM_HAS_V4L2 - Debug( 2, "V4L2 support enabled, using V4L%d api", v4l_version ); - if ( v4l_version == 2 ) - { - struct v4l2_capability vid_cap; - - Debug( 3, "Checking video device capabilities" ); - if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) - Fatal( "Failed to query video device: %s", strerror(errno) ); - - if ( !(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ) - Fatal( "Video device is not video capture device" ); - - if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) - Fatal( "Video device does not support streaming i/o" ); - - Debug( 3, "Setting up video format" ); - - memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); - v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if ( vidioctl( vid_fd, VIDIOC_G_FMT, &v4l2_data.fmt ) < 0 ) - Fatal( "Failed to get video format: %s", strerror(errno) ); - - Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); - Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); - Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); - Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); - Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); - Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); - Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); - Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); - Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); - - v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_data.fmt.fmt.pix.width = width; - v4l2_data.fmt.fmt.pix.height = height; - v4l2_data.fmt.fmt.pix.pixelformat = palette; - - if ( (extras & 0xff) != 0 ) - { - v4l2_data.fmt.fmt.pix.field = (v4l2_field)(extras & 0xff); - - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) + Debug( 2, "V4L2 support enabled, using V4L%d api", v4l_version ); + if ( v4l_version == 2 ) { - Warning( "Failed to set V4L2 field to %d, falling back to auto", (extras & 0xff) ); - v4l2_data.fmt.fmt.pix.field = V4L2_FIELD_ANY; - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { - Fatal( "Failed to set video format: %s", strerror(errno) ); - } - } - } else { - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { - Fatal( "Failed to set video format: %s", strerror(errno) ); - } - } + struct v4l2_capability vid_cap; - /* Note VIDIOC_S_FMT may change width and height. */ - Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); - Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); - Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); - Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); - Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); - Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); - Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); - Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); - Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); + Debug( 3, "Checking video device capabilities" ); + if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) + Fatal( "Failed to query video device: %s", strerror(errno) ); - /* Buggy driver paranoia. */ - unsigned int min; - min = v4l2_data.fmt.fmt.pix.width * 2; - if (v4l2_data.fmt.fmt.pix.bytesperline < min) - v4l2_data.fmt.fmt.pix.bytesperline = min; - min = v4l2_data.fmt.fmt.pix.bytesperline * v4l2_data.fmt.fmt.pix.height; - if (v4l2_data.fmt.fmt.pix.sizeimage < min) - v4l2_data.fmt.fmt.pix.sizeimage = min; + if ( !(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ) + Fatal( "Video device is not video capture device" ); - v4l2_jpegcompression jpeg_comp; - if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { - if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { - if(errno == EINVAL) { - Debug(2, "JPEG compression options are not available"); - } else { - Warning("Failed to get JPEG compression options: %s", strerror(errno) ); - } - } else { - /* Set flags and quality. MJPEG should not have the huffman tables defined */ - if(palette == V4L2_PIX_FMT_MJPEG) { - jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; - } else { - jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; - } - jpeg_comp.quality = 85; - - /* Update the JPEG options */ - if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { - Warning("Failed to set JPEG compression options: %s", strerror(errno) ); - } else { - if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { - Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); + if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) + Fatal( "Video device does not support streaming i/o" ); + + Debug( 3, "Setting up video format" ); + + memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); + v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if ( vidioctl( vid_fd, VIDIOC_G_FMT, &v4l2_data.fmt ) < 0 ) + Fatal( "Failed to get video format: %s", strerror(errno) ); + + Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); + Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); + Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); + Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); + Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); + Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); + Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); + Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); + Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); + + v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_data.fmt.fmt.pix.width = width; + v4l2_data.fmt.fmt.pix.height = height; + v4l2_data.fmt.fmt.pix.pixelformat = palette; + + if ( (extras & 0xff) != 0 ) + { + v4l2_data.fmt.fmt.pix.field = (v4l2_field)(extras & 0xff); + + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) + { + Warning( "Failed to set V4L2 field to %d, falling back to auto", (extras & 0xff) ); + v4l2_data.fmt.fmt.pix.field = V4L2_FIELD_ANY; + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { + Fatal( "Failed to set video format: %s", strerror(errno) ); + } + } + } else { + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { + Fatal( "Failed to set video format: %s", strerror(errno) ); + } + } + + /* Note VIDIOC_S_FMT may change width and height. */ + Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); + Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); + Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); + Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); + Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); + Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); + Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); + Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); + Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); + + /* Buggy driver paranoia. */ + unsigned int min; + min = v4l2_data.fmt.fmt.pix.width * 2; + if (v4l2_data.fmt.fmt.pix.bytesperline < min) + v4l2_data.fmt.fmt.pix.bytesperline = min; + min = v4l2_data.fmt.fmt.pix.bytesperline * v4l2_data.fmt.fmt.pix.height; + if (v4l2_data.fmt.fmt.pix.sizeimage < min) + v4l2_data.fmt.fmt.pix.sizeimage = min; + + v4l2_jpegcompression jpeg_comp; + if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { + if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { + if(errno == EINVAL) { + Debug(2, "JPEG compression options are not available"); + } else { + Warning("Failed to get JPEG compression options: %s", strerror(errno) ); + } + } else { + /* Set flags and quality. MJPEG should not have the huffman tables defined */ + if(palette == V4L2_PIX_FMT_MJPEG) { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + } else { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; + } + jpeg_comp.quality = 85; + + /* Update the JPEG options */ + if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { + Warning("Failed to set JPEG compression options: %s", strerror(errno) ); + } else { + if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { + Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); + } else { + Debug(4, "JPEG quality: %d",jpeg_comp.quality); + Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); + } + } + } + } + + Debug( 3, "Setting up request buffers" ); + + memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); + if ( channel_count > 1 ) { + Debug( 3, "Channel count is %d", channel_count ); + if ( v4l_multi_buffer ){ + v4l2_data.reqbufs.count = 2*channel_count; + } else { + v4l2_data.reqbufs.count = 1; + } } else { - Debug(4, "JPEG quality: %d",jpeg_comp.quality); - Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); + v4l2_data.reqbufs.count = 8; + } + Debug( 3, "Request buffers count is %d", v4l2_data.reqbufs.count ); + + v4l2_data.reqbufs.type = v4l2_data.fmt.type; + v4l2_data.reqbufs.memory = V4L2_MEMORY_MMAP; + + if ( vidioctl( vid_fd, VIDIOC_REQBUFS, &v4l2_data.reqbufs ) < 0 ) + { + if ( errno == EINVAL ) + { + Fatal( "Unable to initialise memory mapping, unsupported in device" ); + } + else + { + Fatal( "Unable to initialise memory mapping: %s", strerror(errno) ); + } } - } - } - } - Debug( 3, "Setting up request buffers" ); - - memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); - if ( channel_count > 1 ) { - Debug( 3, "Channel count is %d", channel_count ); - if ( v4l_multi_buffer ){ - v4l2_data.reqbufs.count = 2*channel_count; - } else { - v4l2_data.reqbufs.count = 1; - } - } else { - v4l2_data.reqbufs.count = 8; - } - Debug( 3, "Request buffers count is %d", v4l2_data.reqbufs.count ); + if ( v4l2_data.reqbufs.count < (v4l_multi_buffer?2:1) ) + Fatal( "Insufficient buffer memory %d on video device", v4l2_data.reqbufs.count ); - v4l2_data.reqbufs.type = v4l2_data.fmt.type; - v4l2_data.reqbufs.memory = V4L2_MEMORY_MMAP; + Debug( 3, "Setting up data buffers: Channels %d MultiBuffer %d Buffers: %d", channel_count, v4l_multi_buffer, v4l2_data.reqbufs.count ); - if ( vidioctl( vid_fd, VIDIOC_REQBUFS, &v4l2_data.reqbufs ) < 0 ) - { - if ( errno == EINVAL ) - { - Fatal( "Unable to initialise memory mapping, unsupported in device" ); - } - else - { - Fatal( "Unable to initialise memory mapping: %s", strerror(errno) ); - } - } - - if ( v4l2_data.reqbufs.count < (v4l_multi_buffer?2:1) ) - Fatal( "Insufficient buffer memory %d on video device", v4l2_data.reqbufs.count ); - - Debug( 3, "Setting up data buffers: Channels %d MultiBuffer %d Buffers: %d", channel_count, v4l_multi_buffer, v4l2_data.reqbufs.count ); - - v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; + v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; #if HAVE_LIBSWSCALE - capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; + capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; #endif // HAVE_LIBSWSCALE - for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) - { - struct v4l2_buffer vid_buf; + for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) + { + struct v4l2_buffer vid_buf; - memset( &vid_buf, 0, sizeof(vid_buf) ); + memset( &vid_buf, 0, sizeof(vid_buf) ); - //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vid_buf.type = v4l2_data.fmt.type; - //vid_buf.memory = V4L2_MEMORY_MMAP; - vid_buf.memory = v4l2_data.reqbufs.memory; - vid_buf.index = i; + //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vid_buf.type = v4l2_data.fmt.type; + //vid_buf.memory = V4L2_MEMORY_MMAP; + vid_buf.memory = v4l2_data.reqbufs.memory; + vid_buf.index = i; - if ( vidioctl( vid_fd, VIDIOC_QUERYBUF, &vid_buf ) < 0 ) - Fatal( "Unable to query video buffer: %s", strerror(errno) ); + if ( vidioctl( vid_fd, VIDIOC_QUERYBUF, &vid_buf ) < 0 ) + Fatal( "Unable to query video buffer: %s", strerror(errno) ); - v4l2_data.buffers[i].length = vid_buf.length; - v4l2_data.buffers[i].start = mmap( NULL, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset ); + v4l2_data.buffers[i].length = vid_buf.length; + v4l2_data.buffers[i].start = mmap( NULL, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset ); - if ( v4l2_data.buffers[i].start == MAP_FAILED ) - Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); + if ( v4l2_data.buffers[i].start == MAP_FAILED ) + Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); #if HAVE_LIBSWSCALE #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - capturePictures[i] = av_frame_alloc(); + capturePictures[i] = av_frame_alloc(); #else - capturePictures[i] = avcodec_alloc_frame(); + capturePictures[i] = avcodec_alloc_frame(); +#endif + if ( !capturePictures[i] ) + Fatal( "Could not allocate picture" ); +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(capturePictures[i]->data, + capturePictures[i]->linesize, + (uint8_t*)v4l2_data.buffers[i].start,capturePixFormat, + v4l2_data.fmt.fmt.pix.width, + v4l2_data.fmt.fmt.pix.height, 1); +#else + avpicture_fill( (AVPicture *)capturePictures[i], + (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, + v4l2_data.fmt.fmt.pix.width, + v4l2_data.fmt.fmt.pix.height ); #endif - if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); - avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height ); #endif // HAVE_LIBSWSCALE + } + + Debug( 3, "Configuring video source" ); + + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channel ) < 0 ) + { + Fatal( "Failed to set camera source %d: %s", channel, strerror(errno) ); + } + + struct v4l2_input input; + v4l2_std_id stdId; + + memset( &input, 0, sizeof(input) ); + + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); + } + + if ( (input.std != V4L2_STD_UNKNOWN) && ((input.std & standard) == V4L2_STD_UNKNOWN) ) + { + Fatal( "Device does not support video standard %d", standard ); + } + + stdId = standard; + if ( (input.std != V4L2_STD_UNKNOWN) && vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) + { + Fatal( "Failed to set video standard %d: %s", standard, strerror(errno) ); + } + + Contrast(contrast); + Brightness(brightness); + Hue(hue); + Colour(colour); } - - Debug( 3, "Configuring video source" ); - - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channel ) < 0 ) - { - Fatal( "Failed to set camera source %d: %s", channel, strerror(errno) ); - } - - struct v4l2_input input; - v4l2_std_id stdId; - - memset( &input, 0, sizeof(input) ); - - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); - } - - if ( (input.std != V4L2_STD_UNKNOWN) && ((input.std & standard) == V4L2_STD_UNKNOWN) ) - { - Fatal( "Device does not support video standard %d", standard ); - } - - stdId = standard; - if ( (input.std != V4L2_STD_UNKNOWN) && vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) - { - Fatal( "Failed to set video standard %d: %s", standard, strerror(errno) ); - } - - Contrast(contrast); - Brightness(brightness); - Hue(hue); - Colour(colour); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - Debug( 3, "Configuring picture attributes" ); - - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(vid_pic) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - Fatal( "Failed to get picture attributes: %s", strerror(errno) ); - - Debug( 4, "Old P:%d", vid_pic.palette ); - Debug( 4, "Old D:%d", vid_pic.depth ); - Debug( 4, "Old B:%d", vid_pic.brightness ); - Debug( 4, "Old h:%d", vid_pic.hue ); - Debug( 4, "Old Cl:%d", vid_pic.colour ); - Debug( 4, "Old Cn:%d", vid_pic.contrast ); - - switch (vid_pic.palette = palette) + if ( v4l_version == 1 ) { - case VIDEO_PALETTE_RGB32 : - { - vid_pic.depth = 32; - break; - } - case VIDEO_PALETTE_RGB24 : - { - vid_pic.depth = 24; - break; - } - case VIDEO_PALETTE_GREY : - { - vid_pic.depth = 8; - break; - } - case VIDEO_PALETTE_RGB565 : - case VIDEO_PALETTE_YUYV : - case VIDEO_PALETTE_YUV422 : - case VIDEO_PALETTE_YUV420P : - case VIDEO_PALETTE_YUV422P : - default: - { - vid_pic.depth = 16; - break; - } - } + Debug( 3, "Configuring picture attributes" ); - if ( brightness >= 0 ) vid_pic.brightness = brightness; - if ( hue >= 0 ) vid_pic.hue = hue; - if ( colour >= 0 ) vid_pic.colour = colour; - if ( contrast >= 0 ) vid_pic.contrast = contrast; + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(vid_pic) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + Fatal( "Failed to get picture attributes: %s", strerror(errno) ); - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); - } + Debug( 4, "Old P:%d", vid_pic.palette ); + Debug( 4, "Old D:%d", vid_pic.depth ); + Debug( 4, "Old B:%d", vid_pic.brightness ); + Debug( 4, "Old h:%d", vid_pic.hue ); + Debug( 4, "Old Cl:%d", vid_pic.colour ); + Debug( 4, "Old Cn:%d", vid_pic.contrast ); - Debug( 3, "Configuring window attributes" ); + switch (vid_pic.palette = palette) + { + case VIDEO_PALETTE_RGB32 : + { + vid_pic.depth = 32; + break; + } + case VIDEO_PALETTE_RGB24 : + { + vid_pic.depth = 24; + break; + } + case VIDEO_PALETTE_GREY : + { + vid_pic.depth = 8; + break; + } + case VIDEO_PALETTE_RGB565 : + case VIDEO_PALETTE_YUYV : + case VIDEO_PALETTE_YUV422 : + case VIDEO_PALETTE_YUV420P : + case VIDEO_PALETTE_YUV422P : + default: + { + vid_pic.depth = 16; + break; + } + } - struct video_window vid_win; - memset( &vid_win, 0, sizeof(vid_win) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) - { - Error( "Failed to get window attributes: %s", strerror(errno) ); - exit(-1); - } - Debug( 4, "Old X:%d", vid_win.x ); - Debug( 4, "Old Y:%d", vid_win.y ); - Debug( 4, "Old W:%d", vid_win.width ); - Debug( 4, "Old H:%d", vid_win.height ); - - vid_win.x = 0; - vid_win.y = 0; - vid_win.width = width; - vid_win.height = height; - vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; + if ( brightness >= 0 ) vid_pic.brightness = brightness; + if ( hue >= 0 ) vid_pic.hue = hue; + if ( colour >= 0 ) vid_pic.colour = colour; + if ( contrast >= 0 ) vid_pic.contrast = contrast; - if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) - { - Error( "Failed to set window attributes: %s", strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); - } + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); + } - Info( "vid_win.width = %08x", vid_win.width ); - Info( "vid_win.height = %08x", vid_win.height ); - Info( "vid_win.flags = %08x", vid_win.flags ); + Debug( 3, "Configuring window attributes" ); - Debug( 3, "Setting up request buffers" ); - if ( ioctl( vid_fd, VIDIOCGMBUF, &v4l1_data.frames ) < 0 ) - Fatal( "Failed to setup memory: %s", strerror(errno) ); - if ( channel_count > 1 && !v4l_multi_buffer ) - v4l1_data.frames.frames = 1; - v4l1_data.buffers = new video_mmap[v4l1_data.frames.frames]; - Debug( 4, "vmb.frames = %d", v4l1_data.frames.frames ); - Debug( 4, "vmb.size = %d", v4l1_data.frames.size ); + struct video_window vid_win; + memset( &vid_win, 0, sizeof(vid_win) ); + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) + { + Error( "Failed to get window attributes: %s", strerror(errno) ); + exit(-1); + } + Debug( 4, "Old X:%d", vid_win.x ); + Debug( 4, "Old Y:%d", vid_win.y ); + Debug( 4, "Old W:%d", vid_win.width ); + Debug( 4, "Old H:%d", vid_win.height ); + + vid_win.x = 0; + vid_win.y = 0; + vid_win.width = width; + vid_win.height = height; + vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; - Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); + if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) + { + Error( "Failed to set window attributes: %s", strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); + } - v4l1_data.bufptr = (unsigned char *)mmap( 0, v4l1_data.frames.size, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, 0 ); - if ( v4l1_data.bufptr == MAP_FAILED ) - Fatal( "Could not mmap video: %s", strerror(errno) ); + Info( "vid_win.width = %08x", vid_win.width ); + Info( "vid_win.height = %08x", vid_win.height ); + Info( "vid_win.flags = %08x", vid_win.flags ); + + Debug( 3, "Setting up request buffers" ); + if ( ioctl( vid_fd, VIDIOCGMBUF, &v4l1_data.frames ) < 0 ) + Fatal( "Failed to setup memory: %s", strerror(errno) ); + if ( channel_count > 1 && !v4l_multi_buffer ) + v4l1_data.frames.frames = 1; + v4l1_data.buffers = new video_mmap[v4l1_data.frames.frames]; + Debug( 4, "vmb.frames = %d", v4l1_data.frames.frames ); + Debug( 4, "vmb.size = %d", v4l1_data.frames.size ); + + Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); + + v4l1_data.bufptr = (unsigned char *)mmap( 0, v4l1_data.frames.size, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, 0 ); + if ( v4l1_data.bufptr == MAP_FAILED ) + Fatal( "Could not mmap video: %s", strerror(errno) ); #if HAVE_LIBSWSCALE - capturePictures = new AVFrame *[v4l1_data.frames.frames]; - for ( int i = 0; i < v4l1_data.frames.frames; i++ ) - { - v4l1_data.buffers[i].frame = i; - v4l1_data.buffers[i].width = width; - v4l1_data.buffers[i].height = height; - v4l1_data.buffers[i].format = palette; + capturePictures = new AVFrame *[v4l1_data.frames.frames]; + for ( int i = 0; i < v4l1_data.frames.frames; i++ ) + { + v4l1_data.buffers[i].frame = i; + v4l1_data.buffers[i].width = width; + v4l1_data.buffers[i].height = height; + v4l1_data.buffers[i].format = palette; #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - capturePictures[i] = av_frame_alloc(); + capturePictures[i] = av_frame_alloc(); #else - capturePictures[i] = avcodec_alloc_frame(); + capturePictures[i] = avcodec_alloc_frame(); #endif - if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); - avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height ); - } + if ( !capturePictures[i] ) + Fatal( "Could not allocate picture" ); +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(capturePictures[i]->data, + capturePictures[i]->linesize, + (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], + capturePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)capturePictures[i], + (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], + capturePixFormat, width, height ); +#endif + } #endif // HAVE_LIBSWSCALE - Debug( 3, "Configuring video source" ); + Debug( 3, "Configuring video source" ); - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(vid_src) ); - vid_src.channel = channel; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) - Fatal( "Failed to get camera source: %s", strerror(errno) ); + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(vid_src) ); + vid_src.channel = channel; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) + Fatal( "Failed to get camera source: %s", strerror(errno) ); - Debug( 4, "Old C:%d", vid_src.channel ); - Debug( 4, "Old F:%d", vid_src.norm ); - Debug( 4, "Old Fl:%x", vid_src.flags ); - Debug( 4, "Old T:%d", vid_src.type ); + Debug( 4, "Old C:%d", vid_src.channel ); + Debug( 4, "Old F:%d", vid_src.norm ); + Debug( 4, "Old Fl:%x", vid_src.flags ); + Debug( 4, "Old T:%d", vid_src.type ); - vid_src.norm = standard; - vid_src.flags = 0; - vid_src.type = VIDEO_TYPE_CAMERA; - if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) - { - Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); + vid_src.norm = standard; + vid_src.flags = 0; + vid_src.type = VIDEO_TYPE_CAMERA; + if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) + { + Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); + } + + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) + Fatal( "Failed to get window data: %s", strerror(errno) ); + + Info( "vid_win.width = %08x", vid_win.width ); + Info( "vid_win.height = %08x", vid_win.height ); + Info( "vid_win.flags = %08x", vid_win.flags ); + + Debug( 4, "New X:%d", vid_win.x ); + Debug( 4, "New Y:%d", vid_win.y ); + Debug( 4, "New W:%d", vid_win.width ); + Debug( 4, "New H:%d", vid_win.height ); + + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + Fatal( "Failed to get window data: %s", strerror(errno) ); + + Debug( 4, "New P:%d", vid_pic.palette ); + Debug( 4, "New D:%d", vid_pic.depth ); + Debug( 4, "New B:%d", vid_pic.brightness ); + Debug( 4, "New h:%d", vid_pic.hue ); + Debug( 4, "New Cl:%d", vid_pic.colour ); + Debug( 4, "New Cn:%d", vid_pic.contrast ); } - - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) - Fatal( "Failed to get window data: %s", strerror(errno) ); - - Info( "vid_win.width = %08x", vid_win.width ); - Info( "vid_win.height = %08x", vid_win.height ); - Info( "vid_win.flags = %08x", vid_win.flags ); - - Debug( 4, "New X:%d", vid_win.x ); - Debug( 4, "New Y:%d", vid_win.y ); - Debug( 4, "New W:%d", vid_win.width ); - Debug( 4, "New H:%d", vid_win.height ); - - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - Fatal( "Failed to get window data: %s", strerror(errno) ); - - Debug( 4, "New P:%d", vid_pic.palette ); - Debug( 4, "New D:%d", vid_pic.depth ); - Debug( 4, "New B:%d", vid_pic.brightness ); - Debug( 4, "New h:%d", vid_pic.hue ); - Debug( 4, "New Cl:%d", vid_pic.colour ); - Debug( 4, "New Cn:%d", vid_pic.contrast ); - } #endif // ZM_HAS_V4L1 } void LocalCamera::Terminate() { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - Debug( 3, "Terminating video stream" ); - //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - // enum v4l2_buf_type type = v4l2_data.fmt.type; - enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; - if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) - Error( "Failed to stop capture stream: %s", strerror(errno) ); + if ( v4l_version == 2 ) + { + Debug( 3, "Terminating video stream" ); + //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + // enum v4l2_buf_type type = v4l2_data.fmt.type; + enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; + if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) + Error( "Failed to stop capture stream: %s", strerror(errno) ); - Debug( 3, "Unmapping video buffers" ); - for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { + Debug( 3, "Unmapping video buffers" ); + for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { #if HAVE_LIBSWSCALE - /* Free capture pictures */ + /* Free capture pictures */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &capturePictures[i] ); + av_frame_free( &capturePictures[i] ); #else - av_freep( &capturePictures[i] ); + av_freep( &capturePictures[i] ); #endif #endif - if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) - Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); - } - - } - else + if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) + Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); + } + + } + else #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { + if ( v4l_version == 1 ) + { #if HAVE_LIBSWSCALE - for(int i=0; i < v4l1_data.frames.frames; i++) { - /* Free capture pictures */ + for(int i=0; i < v4l1_data.frames.frames; i++) { + /* Free capture pictures */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &capturePictures[i] ); + av_frame_free( &capturePictures[i] ); #else - av_freep( &capturePictures[i] ); + av_freep( &capturePictures[i] ); #endif - } + } #endif - - Debug( 3, "Unmapping video buffers" ); - if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) - Error( "Failed to munmap buffers: %s", strerror(errno) ); + + Debug( 3, "Unmapping video buffers" ); + if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) + Error( "Failed to munmap buffers: %s", strerror(errno) ); - delete[] v4l1_data.buffers; - } + delete[] v4l1_data.buffers; + } #endif // ZM_HAS_V4L1 - close( vid_fd ); - + close( vid_fd ); + } uint32_t LocalCamera::AutoSelectFormat(int p_colours) { - /* Automatic format selection */ - uint32_t selected_palette = 0; + /* Automatic format selection */ + uint32_t selected_palette = 0; #if ZM_HAS_V4L2 - char fmt_desc[64][32]; - uint32_t fmt_fcc[64]; - v4l2_fmtdesc fmtinfo; - unsigned int nIndex = 0; - //int nRet = 0; // compiler say it isn't used - int enum_fd; - - /* Open the device */ - if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { - Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); - return selected_palette; - } - - /* Enumerate available formats */ - memset(&fmtinfo, 0, sizeof(fmtinfo)); - fmtinfo.index = nIndex; - fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - // FIXME This will crash if there are more than 64 formats. - while(vidioctl( enum_fd, VIDIOC_ENUM_FMT, &fmtinfo ) >= 0) { - /* Got a format. Copy it to the array */ - strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); - fmt_fcc[nIndex] = fmtinfo.pixelformat; - - Debug(6, "Got format: %s (%c%c%c%c) at index %d",fmt_desc[nIndex],fmt_fcc[nIndex]&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>24)&0xff ,nIndex); - - /* Proceed to the next index */ - memset(&fmtinfo, 0, sizeof(fmtinfo)); - fmtinfo.index = ++nIndex; - fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - - /* Select format */ - int nIndexUsed = -1; - unsigned int n_preferedformats = 0; - const uint32_t* preferedformats; - if(p_colours == ZM_COLOUR_RGB32) { - /* 32bit */ - preferedformats = prefered_rgb32_formats; - n_preferedformats = sizeof(prefered_rgb32_formats) / sizeof(uint32_t); - } else if(p_colours == ZM_COLOUR_GRAY8) { - /* Grayscale */ - preferedformats = prefered_gray8_formats; - n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); - } else { - /* Assume 24bit */ - preferedformats = prefered_rgb24_formats; - n_preferedformats = sizeof(prefered_rgb24_formats) / sizeof(uint32_t); - } - for( unsigned int i=0; i < n_preferedformats && nIndexUsed < 0; i++ ) { - for( unsigned int j=0; j < nIndex; j++ ) { - if( preferedformats[i] == fmt_fcc[j] ) { - Debug(6, "Choosing format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); - /* Found a format! */ - nIndexUsed = j; - break; - } else { - Debug(6, "No match for format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); - } - } - } - - /* Have we found a match? */ - if(nIndexUsed >= 0) { - /* Found a match */ - selected_palette = fmt_fcc[nIndexUsed]; - strcpy(palette_desc,fmt_desc[nIndexUsed]); - } - - /* Close the device */ - close(enum_fd); - + char fmt_desc[64][32]; + uint32_t fmt_fcc[64]; + v4l2_fmtdesc fmtinfo; + unsigned int nIndex = 0; + //int nRet = 0; // compiler say it isn't used + int enum_fd; + + /* Open the device */ + if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { + Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); + return selected_palette; + } + + /* Enumerate available formats */ + memset(&fmtinfo, 0, sizeof(fmtinfo)); + fmtinfo.index = nIndex; + fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + // FIXME This will crash if there are more than 64 formats. + while(vidioctl( enum_fd, VIDIOC_ENUM_FMT, &fmtinfo ) >= 0) { + /* Got a format. Copy it to the array */ + strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); + fmt_fcc[nIndex] = fmtinfo.pixelformat; + + Debug(6, "Got format: %s (%c%c%c%c) at index %d",fmt_desc[nIndex],fmt_fcc[nIndex]&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>24)&0xff ,nIndex); + + /* Proceed to the next index */ + memset(&fmtinfo, 0, sizeof(fmtinfo)); + fmtinfo.index = ++nIndex; + fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + + /* Select format */ + int nIndexUsed = -1; + unsigned int n_preferedformats = 0; + const uint32_t* preferedformats; + if(p_colours == ZM_COLOUR_RGB32) { + /* 32bit */ + preferedformats = prefered_rgb32_formats; + n_preferedformats = sizeof(prefered_rgb32_formats) / sizeof(uint32_t); + } else if(p_colours == ZM_COLOUR_GRAY8) { + /* Grayscale */ + preferedformats = prefered_gray8_formats; + n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); + } else { + /* Assume 24bit */ + preferedformats = prefered_rgb24_formats; + n_preferedformats = sizeof(prefered_rgb24_formats) / sizeof(uint32_t); + } + for( unsigned int i=0; i < n_preferedformats && nIndexUsed < 0; i++ ) { + for( unsigned int j=0; j < nIndex; j++ ) { + if( preferedformats[i] == fmt_fcc[j] ) { + Debug(6, "Choosing format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + /* Found a format! */ + nIndexUsed = j; + break; + } else { + Debug(6, "No match for format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + } + } + } + + /* Have we found a match? */ + if(nIndexUsed >= 0) { + /* Found a match */ + selected_palette = fmt_fcc[nIndexUsed]; + strcpy(palette_desc,fmt_desc[nIndexUsed]); + } + + /* Close the device */ + close(enum_fd); + #endif /* ZM_HAS_V4L2 */ - return selected_palette; + return selected_palette; } #define capString(test,prefix,yesString,noString,capability) \ - (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") + (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) { - output[0] = 0; + output[0] = 0; - char queryDevice[PATH_MAX] = ""; - int devIndex = 0; - do - { - if ( device ) - strcpy( queryDevice, device ); - else - sprintf( queryDevice, "/dev/video%d", devIndex ); - if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) + char queryDevice[PATH_MAX] = ""; + int devIndex = 0; + do { - if ( device ) - { - Error( "Failed to open video device %s: %s", queryDevice, strerror(errno) ); - if ( verbose ) - sprintf( output+strlen(output), "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno) ); + if ( device ) + strcpy( queryDevice, device ); else - sprintf( output+strlen(output), "error%d\n", errno ); - return( false ); - } - else - { - return( true ); - } - } - if ( verbose ) - sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); - else - sprintf( output+strlen(output), "d:%s|", queryDevice ); + sprintf( queryDevice, "/dev/video%d", devIndex ); + if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) + { + if ( device ) + { + Error( "Failed to open video device %s: %s", queryDevice, strerror(errno) ); + if ( verbose ) + sprintf( output+strlen(output), "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno) ); + else + sprintf( output+strlen(output), "error%d\n", errno ); + return( false ); + } + else + { + return( true ); + } + } + if ( verbose ) + sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); + else + sprintf( output+strlen(output), "d:%s|", queryDevice ); #if ZM_HAS_V4L2 - if ( version == 2 ) - { - struct v4l2_capability vid_cap; - if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) - { - Error( "Failed to query video device: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } + if ( version == 2 ) + { + struct v4l2_capability vid_cap; + if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) + { + Error( "Failed to query video device: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } - if ( verbose ) - { - sprintf( output+strlen(output), "General Capabilities\n" ); - sprintf( output+strlen(output), " Driver: %s\n", vid_cap.driver ); - sprintf( output+strlen(output), " Card: %s\n", vid_cap.card ); - sprintf( output+strlen(output), " Bus: %s\n", vid_cap.bus_info ); - sprintf( output+strlen(output), " Version: %u.%u.%u\n", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); - sprintf( output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.capabilities, - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)" ), - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output" ), - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OVERLAY, " ", "Supports", "Does not support", "frame buffer overlay" ), - capString( vid_cap.capabilities&V4L2_CAP_VBI_CAPTURE, " ", "Supports", "Does not support", "VBI capture" ), - capString( vid_cap.capabilities&V4L2_CAP_VBI_OUTPUT, " ", "Supports", "Does not support", "VBI output" ), - capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_CAPTURE, " ", "Supports", "Does not support", "sliced VBI capture" ), - capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_OUTPUT, " ", "Supports", "Does not support", "sliced VBI output" ), + if ( verbose ) + { + sprintf( output+strlen(output), "General Capabilities\n" ); + sprintf( output+strlen(output), " Driver: %s\n", vid_cap.driver ); + sprintf( output+strlen(output), " Card: %s\n", vid_cap.card ); + sprintf( output+strlen(output), " Bus: %s\n", vid_cap.bus_info ); + sprintf( output+strlen(output), " Version: %u.%u.%u\n", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); + sprintf( output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.capabilities, + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OVERLAY, " ", "Supports", "Does not support", "frame buffer overlay" ), + capString( vid_cap.capabilities&V4L2_CAP_VBI_CAPTURE, " ", "Supports", "Does not support", "VBI capture" ), + capString( vid_cap.capabilities&V4L2_CAP_VBI_OUTPUT, " ", "Supports", "Does not support", "VBI output" ), + capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_CAPTURE, " ", "Supports", "Does not support", "sliced VBI capture" ), + capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_OUTPUT, " ", "Supports", "Does not support", "sliced VBI output" ), #ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT_OVERLAY, " ", "Supports", "Does not support", "video output overlay" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT_OVERLAY, " ", "Supports", "Does not support", "video output overlay" ), #else // V4L2_CAP_VIDEO_OUTPUT_OVERLAY - "", + "", #endif // V4L2_CAP_VIDEO_OUTPUT_OVERLAY - capString( vid_cap.capabilities&V4L2_CAP_TUNER, " ", "Has", "Does not have", "tuner" ), - capString( vid_cap.capabilities&V4L2_CAP_AUDIO, " ", "Has", "Does not have", "audio in and/or out" ), - capString( vid_cap.capabilities&V4L2_CAP_RADIO, " ", "Has", "Does not have", "radio" ), - capString( vid_cap.capabilities&V4L2_CAP_READWRITE, " ", "Supports", "Does not support", "read/write i/o (X)" ), - capString( vid_cap.capabilities&V4L2_CAP_ASYNCIO, " ", "Supports", "Does not support", "async i/o" ), - capString( vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)" ) - ); - } - else - { - sprintf( output+strlen(output), "D:%s|", vid_cap.driver ); - sprintf( output+strlen(output), "C:%s|", vid_cap.card ); - sprintf( output+strlen(output), "B:%s|", vid_cap.bus_info ); - sprintf( output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); - sprintf( output+strlen(output), "T:0x%x|", vid_cap.capabilities ); - } - - if ( verbose ) - sprintf( output+strlen(output), " Standards:\n" ); - else - sprintf( output+strlen(output), "S:" ); - struct v4l2_standard standard; - int standardIndex = 0; - do - { - memset( &standard, 0, sizeof(standard) ); - standard.index = standardIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUMSTD, &standard ) < 0 ) - { - if ( errno == EINVAL || errno == ENODATA || errno == ENOTTY ) - { - Debug( 6, "Done enumerating standard %d: %d %s", standard.index, errno, strerror(errno) ); - standardIndex = -1; - break; - } - else - { - Error( "Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno) ); + capString( vid_cap.capabilities&V4L2_CAP_TUNER, " ", "Has", "Does not have", "tuner" ), + capString( vid_cap.capabilities&V4L2_CAP_AUDIO, " ", "Has", "Does not have", "audio in and/or out" ), + capString( vid_cap.capabilities&V4L2_CAP_RADIO, " ", "Has", "Does not have", "radio" ), + capString( vid_cap.capabilities&V4L2_CAP_READWRITE, " ", "Supports", "Does not support", "read/write i/o (X)" ), + capString( vid_cap.capabilities&V4L2_CAP_ASYNCIO, " ", "Supports", "Does not support", "async i/o" ), + capString( vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)" ) + ); + } else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - if ( verbose ) - sprintf( output+strlen(output), " %s\n", standard.name ); - else - sprintf( output+strlen(output), "%s/", standard.name ); - } - while ( standardIndex++ >= 0 ); - if ( !verbose && output[strlen(output)-1] == '/') - output[strlen(output)-1] = '|'; - - if ( verbose ) - sprintf( output+strlen(output), " Formats:\n" ); - else - sprintf( output+strlen(output), "F:" ); - struct v4l2_fmtdesc format; - int formatIndex = 0; - do - { - memset( &format, 0, sizeof(format) ); - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - format.index = formatIndex; + { + sprintf( output+strlen(output), "D:%s|", vid_cap.driver ); + sprintf( output+strlen(output), "C:%s|", vid_cap.card ); + sprintf( output+strlen(output), "B:%s|", vid_cap.bus_info ); + sprintf( output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); + sprintf( output+strlen(output), "T:0x%x|", vid_cap.capabilities ); + } - if ( vidioctl( vid_fd, VIDIOC_ENUM_FMT, &format ) < 0 ) - { - if ( errno == EINVAL ) - { - formatIndex = -1; - break; - } - else - { - Error( "Failed to enumerate format %d: %s", format.index, strerror(errno) ); if ( verbose ) - sprintf( output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno) ); + sprintf( output+strlen(output), " Standards:\n" ); else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - if ( verbose ) - sprintf( output+strlen(output), " %s (%c%c%c%c)\n", format.description, format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); - else - sprintf( output+strlen(output), "%c%c%c%c/", format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); - } - while ( formatIndex++ >= 0 ); - if ( !verbose ) - output[strlen(output)-1] = '|'; + sprintf( output+strlen(output), "S:" ); + struct v4l2_standard standard; + int standardIndex = 0; + do + { + memset( &standard, 0, sizeof(standard) ); + standard.index = standardIndex; - if(verbose) - sprintf( output+strlen(output), "Crop Capabilities\n" ); - - struct v4l2_cropcap cropcap; - memset( &cropcap, 0, sizeof(cropcap) ); - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) - { - if(errno != EINVAL) { - /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ - Error( "Failed to query crop capabilities: %s", strerror(errno) ); - } + if ( vidioctl( vid_fd, VIDIOC_ENUMSTD, &standard ) < 0 ) + { + if ( errno == EINVAL || errno == ENODATA || errno == ENOTTY ) + { + Debug( 6, "Done enumerating standard %d: %d %s", standard.index, errno, strerror(errno) ); + standardIndex = -1; + break; + } + else + { + Error( "Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + if ( verbose ) + sprintf( output+strlen(output), " %s\n", standard.name ); + else + sprintf( output+strlen(output), "%s/", standard.name ); + } + while ( standardIndex++ >= 0 ); + if ( !verbose && output[strlen(output)-1] == '/') + output[strlen(output)-1] = '|'; - if(verbose) { - sprintf( output+strlen(output), " Cropping is not supported\n"); - } else { - /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf( output+strlen(output), "B:%dx%d|",0,0); - } - } else { - struct v4l2_crop crop; - memset( &crop, 0, sizeof(crop) ); - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if ( vidioctl( vid_fd, VIDIOC_G_CROP, &crop ) < 0 ) - { - if ( errno != EINVAL ) - { - /* Failed querying crop sizes, write error to the log and continue as if crop is not supported */ - Error( "Failed to query crop: %s", strerror(errno) ); - } - - if ( verbose ) { - sprintf( output+strlen(output), " Cropping is not supported\n"); - } else { - /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf( output+strlen(output), "B:%dx%d|",0,0); - } - } else { - /* Cropping supported */ - if ( verbose ) { - sprintf( output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height ); - sprintf( output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height ); - sprintf( output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height ); - } else { - sprintf( output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height ); - } - } - } /* Crop code */ - - struct v4l2_input input; - int inputIndex = 0; - do - { - memset( &input, 0, sizeof(input) ); - input.index = inputIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { - break; - } - else - { - Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); if ( verbose ) - sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + sprintf( output+strlen(output), " Formats:\n" ); else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - } - while ( inputIndex++ >= 0 ); + sprintf( output+strlen(output), "F:" ); + struct v4l2_fmtdesc format; + int formatIndex = 0; + do + { + memset( &format, 0, sizeof(format) ); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + format.index = formatIndex; - if ( verbose ) - sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); - else - sprintf( output+strlen(output), "I:%d|", inputIndex ); + if ( vidioctl( vid_fd, VIDIOC_ENUM_FMT, &format ) < 0 ) + { + if ( errno == EINVAL ) + { + formatIndex = -1; + break; + } + else + { + Error( "Failed to enumerate format %d: %s", format.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + if ( verbose ) + sprintf( output+strlen(output), " %s (%c%c%c%c)\n", format.description, format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); + else + sprintf( output+strlen(output), "%c%c%c%c/", format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); + } + while ( formatIndex++ >= 0 ); + if ( !verbose ) + output[strlen(output)-1] = '|'; - inputIndex = 0; - do - { - memset( &input, 0, sizeof(input) ); - input.index = inputIndex; + if(verbose) + sprintf( output+strlen(output), "Crop Capabilities\n" ); + + struct v4l2_cropcap cropcap; + memset( &cropcap, 0, sizeof(cropcap) ); + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) + { + if(errno != EINVAL) { + /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ + Error( "Failed to query crop capabilities: %s", strerror(errno) ); + } + + if(verbose) { + sprintf( output+strlen(output), " Cropping is not supported\n"); + } else { + /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ + sprintf( output+strlen(output), "B:%dx%d|",0,0); + } + } else { + struct v4l2_crop crop; + memset( &crop, 0, sizeof(crop) ); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if ( vidioctl( vid_fd, VIDIOC_G_CROP, &crop ) < 0 ) + { + if ( errno != EINVAL ) + { + /* Failed querying crop sizes, write error to the log and continue as if crop is not supported */ + Error( "Failed to query crop: %s", strerror(errno) ); + } + + if ( verbose ) { + sprintf( output+strlen(output), " Cropping is not supported\n"); + } else { + /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ + sprintf( output+strlen(output), "B:%dx%d|",0,0); + } + } else { + /* Cropping supported */ + if ( verbose ) { + sprintf( output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height ); + sprintf( output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height ); + sprintf( output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height ); + } else { + sprintf( output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height ); + } + } + } /* Crop code */ + + struct v4l2_input input; + int inputIndex = 0; + do + { + memset( &input, 0, sizeof(input) ); + input.index = inputIndex; + + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + if ( errno == EINVAL ) + { + break; + } + else + { + Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + } + while ( inputIndex++ >= 0 ); - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { - inputIndex = -1; - break; - } - else - { - Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); if ( verbose ) - sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } + sprintf( output+strlen(output), "I:%d|", inputIndex ); - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &input.index ) < 0 ) - { - Error( "Failed to set video input %d: %s", input.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } + inputIndex = 0; + do + { + memset( &input, 0, sizeof(input) ); + input.index = inputIndex; - if ( verbose ) - { - sprintf( output+strlen(output), " Input %d\n", input.index ); - sprintf( output+strlen(output), " Name: %s\n", input.name ); - sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); - sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); - } - else - { - sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); - sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); - } + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + if ( errno == EINVAL ) + { + inputIndex = -1; + break; + } + else + { + Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } - if ( verbose ) - { - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &input.index ) < 0 ) + { + Error( "Failed to set video input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + + if ( verbose ) + { + sprintf( output+strlen(output), " Input %d\n", input.index ); + sprintf( output+strlen(output), " Name: %s\n", input.name ); + sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); + sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); + sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); + } + else + { + sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); + sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); + sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); + } + + if ( verbose ) + { + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); + } + else + { + sprintf( output+strlen(output), "i%dSP:%d|", input.index, input.status&V4L2_IN_ST_NO_POWER?0:1 ); + sprintf( output+strlen(output), "i%dSS:%d|", input.index, input.status&V4L2_IN_ST_NO_SIGNAL?0:1 ); + sprintf( output+strlen(output), "i%dSC:%d|", input.index, input.status&V4L2_IN_ST_NO_COLOR?0:1 ); + sprintf( output+strlen(output), "i%dHP:%d|", input.index, input.status&V4L2_IN_ST_NO_H_LOCK?0:1 ); + } + } + while ( inputIndex++ >= 0 ); + if ( !verbose ) + output[strlen(output)-1] = '\n'; } - else - { - sprintf( output+strlen(output), "i%dSP:%d|", input.index, input.status&V4L2_IN_ST_NO_POWER?0:1 ); - sprintf( output+strlen(output), "i%dSS:%d|", input.index, input.status&V4L2_IN_ST_NO_SIGNAL?0:1 ); - sprintf( output+strlen(output), "i%dSC:%d|", input.index, input.status&V4L2_IN_ST_NO_COLOR?0:1 ); - sprintf( output+strlen(output), "i%dHP:%d|", input.index, input.status&V4L2_IN_ST_NO_H_LOCK?0:1 ); - } - } - while ( inputIndex++ >= 0 ); - if ( !verbose ) - output[strlen(output)-1] = '\n'; - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( version == 1 ) - { - struct video_capability vid_cap; - memset( &vid_cap, 0, sizeof(video_capability) ); - if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) - { - Error( "Failed to get video capabilities: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Video Capabilities\n" ); - sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); - sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, - vid_cap.type&VID_TYPE_CAPTURE?" Can capture\n":"", - vid_cap.type&VID_TYPE_TUNER?" Can tune\n":"", - vid_cap.type&VID_TYPE_TELETEXT?" Does teletext\n":"", - vid_cap.type&VID_TYPE_OVERLAY?" Overlay onto frame buffer\n":"", - vid_cap.type&VID_TYPE_CHROMAKEY?" Overlay by chromakey\n":"", - vid_cap.type&VID_TYPE_CLIPPING?" Can clip\n":"", - vid_cap.type&VID_TYPE_FRAMERAM?" Uses the frame buffer memory\n":"", - vid_cap.type&VID_TYPE_SCALES?" Scalable\n":"", - vid_cap.type&VID_TYPE_MONOCHROME?" Monochrome only\n":"", - vid_cap.type&VID_TYPE_SUBCAPTURE?" Can capture subareas of the image\n":"", - vid_cap.type&VID_TYPE_MPEG_DECODER?" Can decode MPEG streams\n":"", - vid_cap.type&VID_TYPE_MPEG_ENCODER?" Can encode MPEG streams\n":"", - vid_cap.type&VID_TYPE_MJPEG_DECODER?" Can decode MJPEG streams\n":"", - vid_cap.type&VID_TYPE_MJPEG_ENCODER?" Can encode MJPEG streams\n":"" - ); - sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); - sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); - sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); - sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); - sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); - sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); - } - else - { - sprintf( output+strlen(output), "N:%s|", vid_cap.name ); - sprintf( output+strlen(output), "T:%d|", vid_cap.type ); - sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); - sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); - sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); - sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); - sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); - sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); - } - - struct video_window vid_win; - memset( &vid_win, 0, sizeof(video_window) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) - { - Error( "Failed to get window attributes: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Window Attributes\n" ); - sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); - sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); - sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); - sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); - } - else - { - sprintf( output+strlen(output), "X:%d|", vid_win.x ); - sprintf( output+strlen(output), "Y:%d|", vid_win.y ); - sprintf( output+strlen(output), "W:%d|", vid_win.width ); - sprintf( output+strlen(output), "H:%d|", vid_win.height ); - } - - struct video_picture vid_pic; - memset( &vid_cap, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Picture Attributes\n" ); - sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, - vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( - vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( - vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB24?"24bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB32?"32bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB555?"555 15bit RGB":( - vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422 capture":( - vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( - vid_pic.palette==VIDEO_PALETTE_UYVY?"UVYV":( - vid_pic.palette==VIDEO_PALETTE_YUV420?"YUV420":( - vid_pic.palette==VIDEO_PALETTE_YUV411?"YUV411 capture":( - vid_pic.palette==VIDEO_PALETTE_RAW?"RAW capture (BT848)":( - vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( - vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422":( - vid_pic.palette==VIDEO_PALETTE_YUV422P?"YUV 4:2:2 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" - )))))))))))))))))); - sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); - sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); - sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); - sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); - sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); - sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); - } - else - { - sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); - sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); - sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); - sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); - sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); - sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); - sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); - } - - for ( int chan = 0; chan < vid_cap.channels; chan++ ) - { - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(video_channel) ); - vid_src.channel = chan; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) + if ( version == 1 ) { - Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); + struct video_capability vid_cap; + memset( &vid_cap, 0, sizeof(video_capability) ); + if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) + { + Error( "Failed to get video capabilities: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Video Capabilities\n" ); + sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); + sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, + vid_cap.type&VID_TYPE_CAPTURE?" Can capture\n":"", + vid_cap.type&VID_TYPE_TUNER?" Can tune\n":"", + vid_cap.type&VID_TYPE_TELETEXT?" Does teletext\n":"", + vid_cap.type&VID_TYPE_OVERLAY?" Overlay onto frame buffer\n":"", + vid_cap.type&VID_TYPE_CHROMAKEY?" Overlay by chromakey\n":"", + vid_cap.type&VID_TYPE_CLIPPING?" Can clip\n":"", + vid_cap.type&VID_TYPE_FRAMERAM?" Uses the frame buffer memory\n":"", + vid_cap.type&VID_TYPE_SCALES?" Scalable\n":"", + vid_cap.type&VID_TYPE_MONOCHROME?" Monochrome only\n":"", + vid_cap.type&VID_TYPE_SUBCAPTURE?" Can capture subareas of the image\n":"", + vid_cap.type&VID_TYPE_MPEG_DECODER?" Can decode MPEG streams\n":"", + vid_cap.type&VID_TYPE_MPEG_ENCODER?" Can encode MPEG streams\n":"", + vid_cap.type&VID_TYPE_MJPEG_DECODER?" Can decode MJPEG streams\n":"", + vid_cap.type&VID_TYPE_MJPEG_ENCODER?" Can encode MJPEG streams\n":"" + ); + sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); + sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); + sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); + sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); + sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); + sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); + } + else + { + sprintf( output+strlen(output), "N:%s|", vid_cap.name ); + sprintf( output+strlen(output), "T:%d|", vid_cap.type ); + sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); + sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); + sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); + sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); + sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); + sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); + } + + struct video_window vid_win; + memset( &vid_win, 0, sizeof(video_window) ); + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) + { + Error( "Failed to get window attributes: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Window Attributes\n" ); + sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); + sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); + sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); + sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); + } + else + { + sprintf( output+strlen(output), "X:%d|", vid_win.x ); + sprintf( output+strlen(output), "Y:%d|", vid_win.y ); + sprintf( output+strlen(output), "W:%d|", vid_win.width ); + sprintf( output+strlen(output), "H:%d|", vid_win.height ); + } + + struct video_picture vid_pic; + memset( &vid_cap, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Picture Attributes\n" ); + sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, + vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( + vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( + vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB24?"24bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB32?"32bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB555?"555 15bit RGB":( + vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422 capture":( + vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( + vid_pic.palette==VIDEO_PALETTE_UYVY?"UVYV":( + vid_pic.palette==VIDEO_PALETTE_YUV420?"YUV420":( + vid_pic.palette==VIDEO_PALETTE_YUV411?"YUV411 capture":( + vid_pic.palette==VIDEO_PALETTE_RAW?"RAW capture (BT848)":( + vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( + vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422":( + vid_pic.palette==VIDEO_PALETTE_YUV422P?"YUV 4:2:2 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" + )))))))))))))))))); + sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); + sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); + sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); + sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); + sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); + sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); + } + else + { + sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); + sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); + sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); + sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); + sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); + sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); + sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); + } + + for ( int chan = 0; chan < vid_cap.channels; chan++ ) + { + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(video_channel) ); + vid_src.channel = chan; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) + { + Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); + sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); + sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); + sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, + vid_src.flags&VIDEO_VC_TUNER?" Channel has a tuner\n":"", + vid_src.flags&VIDEO_VC_AUDIO?" Channel has audio\n":"" + ); + sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, + vid_src.type==VIDEO_TYPE_TV?"TV":( + vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" + )); + sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, + vid_src.norm==VIDEO_MODE_PAL?"PAL":( + vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( + vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( + vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" + )))); + } + else + { + sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); + sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); + sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); + sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); + sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); + } + } + if ( !verbose ) + output[strlen(output)-1] = '\n'; } - if ( verbose ) - { - sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); - sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); - sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); - sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, - vid_src.flags&VIDEO_VC_TUNER?" Channel has a tuner\n":"", - vid_src.flags&VIDEO_VC_AUDIO?" Channel has audio\n":"" - ); - sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, - vid_src.type==VIDEO_TYPE_TV?"TV":( - vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" - )); - sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, - vid_src.norm==VIDEO_MODE_PAL?"PAL":( - vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( - vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( - vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" - )))); - } - else - { - sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); - sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); - sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); - sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); - sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); - } - } - if ( !verbose ) - output[strlen(output)-1] = '\n'; - } #endif // ZM_HAS_V4L1 - close( vid_fd ); - if ( device ) - break; - } - while ( ++devIndex < 32 ); - return( true ); + close( vid_fd ); + if ( device ) + break; + } + while ( ++devIndex < 32 ); + return( true ); } int LocalCamera::Brightness( int p_brightness ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_BRIGHTNESS; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + if ( v4l_version == 2 ) { - if ( errno != EINVAL ) - Error( "Unable to query brightness: %s", strerror(errno) ) - else - Warning( "Brightness control is not supported" ) - //Info( "Brightness 1 %d", vid_control.value ); - } - else if ( p_brightness >= 0 ) - { - vid_control.value = p_brightness; + struct v4l2_control vid_control; - //Info( "Brightness 2 %d", vid_control.value ); - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) - { - if ( errno != ERANGE ) - Error( "Unable to set brightness: %s", strerror(errno) ) - else - Warning( "Given brightness value (%d) may be out-of-range", p_brightness ) - } - //Info( "Brightness 3 %d", vid_control.value ); + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_BRIGHTNESS; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + { + if ( errno != EINVAL ) + Error( "Unable to query brightness: %s", strerror(errno) ) + else + Warning( "Brightness control is not supported" ) + //Info( "Brightness 1 %d", vid_control.value ); + } + else if ( p_brightness >= 0 ) + { + vid_control.value = p_brightness; + + //Info( "Brightness 2 %d", vid_control.value ); + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) + { + if ( errno != ERANGE ) + Error( "Unable to set brightness: %s", strerror(errno) ) + else + Warning( "Given brightness value (%d) may be out-of-range", p_brightness ) + } + //Info( "Brightness 3 %d", vid_control.value ); + } + return( vid_control.value ); } - return( vid_control.value ); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + if ( v4l_version == 1 ) { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); + } - if ( p_brightness >= 0 ) - { - vid_pic.brightness = p_brightness; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } + if ( p_brightness >= 0 ) + { + vid_pic.brightness = p_brightness; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.brightness ); } - return( vid_pic.brightness ); - } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Hue( int p_hue ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_HUE; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + if ( v4l_version == 2 ) { - if ( errno != EINVAL ) - Error( "Unable to query hue: %s", strerror(errno) ) - else - Warning( "Hue control is not supported" ) - } - else if ( p_hue >= 0 ) - { - vid_control.value = p_hue; + struct v4l2_control vid_control; - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) - { - if ( errno != ERANGE ) - Error( "Unable to set hue: %s", strerror(errno) ) - else - Warning( "Given hue value (%d) may be out-of-range", p_hue ) - } + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_HUE; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + { + if ( errno != EINVAL ) + Error( "Unable to query hue: %s", strerror(errno) ) + else + Warning( "Hue control is not supported" ) + } + else if ( p_hue >= 0 ) + { + vid_control.value = p_hue; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) + { + if ( errno != ERANGE ) + Error( "Unable to set hue: %s", strerror(errno) ) + else + Warning( "Given hue value (%d) may be out-of-range", p_hue ) + } + } + return( vid_control.value ); } - return( vid_control.value ); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + if ( v4l_version == 1 ) { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); + } - if ( p_hue >= 0 ) - { - vid_pic.hue = p_hue; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } + if ( p_hue >= 0 ) + { + vid_pic.hue = p_hue; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.hue ); } - return( vid_pic.hue ); - } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Colour( int p_colour ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_SATURATION; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + if ( v4l_version == 2 ) { - if ( errno != EINVAL ) - Error( "Unable to query saturation: %s", strerror(errno) ) - else - Warning( "Saturation control is not supported" ) - } - else if ( p_colour >= 0 ) - { - vid_control.value = p_colour; + struct v4l2_control vid_control; - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) - { - if ( errno != ERANGE ) - Error( "Unable to set saturation: %s", strerror(errno) ) - else - Warning( "Given saturation value (%d) may be out-of-range", p_colour ) - } + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_SATURATION; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + { + if ( errno != EINVAL ) + Error( "Unable to query saturation: %s", strerror(errno) ) + else + Warning( "Saturation control is not supported" ) + } + else if ( p_colour >= 0 ) + { + vid_control.value = p_colour; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) + { + if ( errno != ERANGE ) + Error( "Unable to set saturation: %s", strerror(errno) ) + else + Warning( "Given saturation value (%d) may be out-of-range", p_colour ) + } + } + return( vid_control.value ); } - return( vid_control.value ); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + if ( v4l_version == 1 ) { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); + } - if ( p_colour >= 0 ) - { - vid_pic.colour = p_colour; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } + if ( p_colour >= 0 ) + { + vid_pic.colour = p_colour; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.colour ); } - return( vid_pic.colour ); - } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Contrast( int p_contrast ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_CONTRAST; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + if ( v4l_version == 2 ) { - if ( errno != EINVAL ) - Error( "Unable to query contrast: %s", strerror(errno) ) - else - Warning( "Contrast control is not supported" ) - } - else if ( p_contrast >= 0 ) - { - vid_control.value = p_contrast; + struct v4l2_control vid_control; - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) - { - if ( errno != ERANGE ) - Error( "Unable to set contrast: %s", strerror(errno) ) - else - Warning( "Given contrast value (%d) may be out-of-range", p_contrast ) - } + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_CONTRAST; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) + { + if ( errno != EINVAL ) + Error( "Unable to query contrast: %s", strerror(errno) ) + else + Warning( "Contrast control is not supported" ) + } + else if ( p_contrast >= 0 ) + { + vid_control.value = p_contrast; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) + { + if ( errno != ERANGE ) + Error( "Unable to set contrast: %s", strerror(errno) ) + else + Warning( "Given contrast value (%d) may be out-of-range", p_contrast ) + } + } + return( vid_control.value ); } - return( vid_control.value ); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + if ( v4l_version == 1 ) { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); + } - if ( p_contrast >= 0 ) - { - vid_pic.contrast = p_contrast; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } + if ( p_contrast >= 0 ) + { + vid_pic.contrast = p_contrast; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.contrast ); } - return( vid_pic.contrast ); - } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::PrimeCapture() { - Initialise(); + Initialise(); - Debug( 2, "Priming capture" ); + Debug( 2, "Priming capture" ); #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - Debug( 3, "Queueing buffers" ); - for ( unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++ ) + if ( v4l_version == 2 ) { - struct v4l2_buffer vid_buf; + Debug( 3, "Queueing buffers" ); + for ( unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++ ) + { + struct v4l2_buffer vid_buf; - memset( &vid_buf, 0, sizeof(vid_buf) ); + memset( &vid_buf, 0, sizeof(vid_buf) ); - vid_buf.type = v4l2_data.fmt.type; - vid_buf.memory = v4l2_data.reqbufs.memory; - vid_buf.index = frame; + vid_buf.type = v4l2_data.fmt.type; + vid_buf.memory = v4l2_data.reqbufs.memory; + vid_buf.index = frame; - if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) - Fatal( "Failed to queue buffer %d: %s", frame, strerror(errno) ); + if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) + Fatal( "Failed to queue buffer %d: %s", frame, strerror(errno) ); + } + v4l2_data.bufptr = NULL; + + Debug( 3, "Starting video stream" ); + //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + //enum v4l2_buf_type type = v4l2_data.fmt.type; + enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; + if ( vidioctl( vid_fd, VIDIOC_STREAMON, &type ) < 0 ) + Fatal( "Failed to start capture stream: %s", strerror(errno) ); } - v4l2_data.bufptr = NULL; - - Debug( 3, "Starting video stream" ); - //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - //enum v4l2_buf_type type = v4l2_data.fmt.type; - enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; - if ( vidioctl( vid_fd, VIDIOC_STREAMON, &type ) < 0 ) - Fatal( "Failed to start capture stream: %s", strerror(errno) ); - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - for ( int frame = 0; frame < v4l1_data.frames.frames; frame++ ) + if ( v4l_version == 1 ) { - Debug( 3, "Queueing frame %d", frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[frame] ) < 0 ) - { - Error( "Capture failure for frame %d: %s", frame, strerror(errno) ); - return( -1 ); - } + for ( int frame = 0; frame < v4l1_data.frames.frames; frame++ ) + { + Debug( 3, "Queueing frame %d", frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[frame] ) < 0 ) + { + Error( "Capture failure for frame %d: %s", frame, strerror(errno) ); + return( -1 ); + } + } } - } #endif // ZM_HAS_V4L1 - return( 0 ); + return( 0 ); } int LocalCamera::PreCapture() { - Debug( 2, "Pre-capturing" ); - return( 0 ); + Debug( 2, "Pre-capturing" ); + return( 0 ); } int LocalCamera::Capture( Image &image ) { - Debug( 3, "Capturing" ); - static uint8_t* buffer = NULL; - static uint8_t* directbuffer = NULL; - static int capture_frame = -1; - int buffer_bytesused = 0; - - int captures_per_frame = 1; - if ( channel_count > 1 ) - captures_per_frame = v4l_captures_per_frame; - if ( captures_per_frame <= 0 ) { - captures_per_frame = 1; - Warning( "Invalid Captures Per Frame setting: %d", captures_per_frame ); - } - - - // Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer - if ( channel_prime ) - { -#if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + Debug( 3, "Capturing" ); + static uint8_t* buffer = NULL; + static uint8_t* directbuffer = NULL; + static int capture_frame = -1; + int buffer_bytesused = 0; + + int captures_per_frame = 1; + if ( channel_count > 1 ) + captures_per_frame = v4l_captures_per_frame; + if ( captures_per_frame <= 0 ) { + captures_per_frame = 1; + Warning( "Invalid Captures Per Frame setting: %d", captures_per_frame ); + } + + + // Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer + if ( channel_prime ) { - static struct v4l2_buffer vid_buf; - - memset( &vid_buf, 0, sizeof(vid_buf) ); - - vid_buf.type = v4l2_data.fmt.type; - //vid_buf.memory = V4L2_MEMORY_MMAP; - vid_buf.memory = v4l2_data.reqbufs.memory; - - Debug( 3, "Capturing %d frames", captures_per_frame ); - while ( captures_per_frame ) - { - if ( vidioctl( vid_fd, VIDIOC_DQBUF, &vid_buf ) < 0 ) +#if ZM_HAS_V4L2 + if ( v4l_version == 2 ) { - if ( errno == EIO ) - Warning( "Capture failure, possible signal loss?: %s", strerror(errno) ) - else - Error( "Unable to capture frame %d: %s", vid_buf.index, strerror(errno) ) - return( -1 ); + static struct v4l2_buffer vid_buf; + + memset( &vid_buf, 0, sizeof(vid_buf) ); + + vid_buf.type = v4l2_data.fmt.type; + //vid_buf.memory = V4L2_MEMORY_MMAP; + vid_buf.memory = v4l2_data.reqbufs.memory; + + Debug( 3, "Capturing %d frames", captures_per_frame ); + while ( captures_per_frame ) + { + if ( vidioctl( vid_fd, VIDIOC_DQBUF, &vid_buf ) < 0 ) + { + if ( errno == EIO ) + Warning( "Capture failure, possible signal loss?: %s", strerror(errno) ) + else + Error( "Unable to capture frame %d: %s", vid_buf.index, strerror(errno) ) + return( -1 ); + } + + v4l2_data.bufptr = &vid_buf; + capture_frame = v4l2_data.bufptr->index; + if ( --captures_per_frame ) + { + if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) + { + Error( "Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno) ); + return( -1 ); + } + } + } + + Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); + + buffer = (unsigned char *)v4l2_data.buffers[v4l2_data.bufptr->index].start; + buffer_bytesused = v4l2_data.bufptr->bytesused; + + if((v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height)) { + Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",v4l2_data.fmt.fmt.pix.width,v4l2_data.fmt.fmt.pix.height,width,height); + } + } - - v4l2_data.bufptr = &vid_buf; - capture_frame = v4l2_data.bufptr->index; - if ( --captures_per_frame ) - { - if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) - { - Error( "Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno) ); - return( -1 ); - } - } - } - - Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); - - buffer = (unsigned char *)v4l2_data.buffers[v4l2_data.bufptr->index].start; - buffer_bytesused = v4l2_data.bufptr->bytesused; - - if((v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height)) { - Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",v4l2_data.fmt.fmt.pix.width,v4l2_data.fmt.fmt.pix.height,width,height); - } - - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - Debug( 3, "Capturing %d frames", captures_per_frame ); - while ( captures_per_frame ) - { - Debug( 3, "Syncing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCSYNC, &v4l1_data.active_frame ) < 0 ) + if ( v4l_version == 1 ) { - Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); - return( -1 ); - } - captures_per_frame--; - if ( captures_per_frame ) - { - Debug( 3, "Capturing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) - { - Error( "Capture failure for buffer %d (%d): %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); - return( -1 ); - } - } - } - capture_frame = v4l1_data.active_frame; - Debug( 3, "Captured %d for channel %d", capture_frame, channel ); + Debug( 3, "Capturing %d frames", captures_per_frame ); + while ( captures_per_frame ) + { + Debug( 3, "Syncing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCSYNC, &v4l1_data.active_frame ) < 0 ) + { + Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); + return( -1 ); + } + captures_per_frame--; + if ( captures_per_frame ) + { + Debug( 3, "Capturing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) + { + Error( "Capture failure for buffer %d (%d): %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); + return( -1 ); + } + } + } + capture_frame = v4l1_data.active_frame; + Debug( 3, "Captured %d for channel %d", capture_frame, channel ); - buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; - } + buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; + } #endif // ZM_HAS_V4L1 - } /* prime capture */ - - if(conversion_type != 0) { + } /* prime capture */ - Debug( 3, "Performing format conversion" ); - - /* 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."); - return (-1); - } + if(conversion_type != 0) { + + Debug( 3, "Performing format conversion" ); + + /* 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."); + return (-1); + } #if HAVE_LIBSWSCALE - if(conversion_type == 1) { - - Debug( 9, "Calling sws_scale to perform the conversion" ); - /* Use swscale to convert the image directly into the shared memory */ - avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); - sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); - } -#endif - if(conversion_type == 2) { - - Debug( 9, "Calling the conversion function" ); - /* Call the image conversion function and convert directly into the shared memory */ - (*conversion_fptr)(buffer, directbuffer, pixels); - } - else if(conversion_type == 3) { - - Debug( 9, "Decoding the JPEG image" ); - /* JPEG decoding */ - image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); - } - - } else { - Debug( 3, "No format conversion performed. Assigning the image" ); - - /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ - image.Assign( width, height, colours, subpixelorder, buffer, imagesize); - - } - - return( 0 ); + if(conversion_type == 1) { + + Debug( 9, "Calling sws_scale to perform the conversion" ); + /* Use swscale to convert the image directly into the shared memory */ +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(tmpPicture->data, + tmpPicture->linesize, directbuffer, + imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)tmpPicture, directbuffer, + imagePixFormat, width, height ); +#endif + sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); + } +#endif + if(conversion_type == 2) { + + Debug( 9, "Calling the conversion function" ); + /* Call the image conversion function and convert directly into the shared memory */ + (*conversion_fptr)(buffer, directbuffer, pixels); + } + else if(conversion_type == 3) { + + Debug( 9, "Decoding the JPEG image" ); + /* JPEG decoding */ + image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); + } + + } else { + Debug( 3, "No format conversion performed. Assigning the image" ); + + /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ + image.Assign( width, height, colours, subpixelorder, buffer, imagesize); + + } + + return( 0 ); } int LocalCamera::PostCapture() { - Debug( 2, "Post-capturing" ); - // Requeue the buffer unless we need to switch or are a duplicate camera on a channel - if ( channel_count > 1 || channel_prime ) - { -#if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + Debug( 2, "Post-capturing" ); + // Requeue the buffer unless we need to switch or are a duplicate camera on a channel + if ( channel_count > 1 || channel_prime ) { - if ( channel_count > 1 ) - { - int next_channel = (channel_index+1)%channel_count; - Debug( 3, "Switching video source to %d", channels[next_channel] ); - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channels[next_channel] ) < 0 ) +#if ZM_HAS_V4L2 + if ( v4l_version == 2 ) { - Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); - return( -1 ); - } + if ( channel_count > 1 ) + { + int next_channel = (channel_index+1)%channel_count; + Debug( 3, "Switching video source to %d", channels[next_channel] ); + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channels[next_channel] ) < 0 ) + { + Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); + return( -1 ); + } - v4l2_std_id stdId = standards[next_channel]; - if ( vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) - { - Error( "Failed to set video format %d: %s", standards[next_channel], strerror(errno) ); - return( -1 ); + v4l2_std_id stdId = standards[next_channel]; + if ( vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) + { + Error( "Failed to set video format %d: %s", standards[next_channel], strerror(errno) ); + return( -1 ); + } + } + if ( v4l2_data.bufptr ) { + Debug( 3, "Requeueing buffer %d", v4l2_data.bufptr->index ); + if ( vidioctl( vid_fd, VIDIOC_QBUF, v4l2_data.bufptr ) < 0 ) + { + Error( "Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno) ) + return( -1 ); + } + } else { + Error( "Unable to requeue buffer due to not v4l2_data" ) + } } - } - if ( v4l2_data.bufptr ) { - Debug( 3, "Requeueing buffer %d", v4l2_data.bufptr->index ); - if ( vidioctl( vid_fd, VIDIOC_QBUF, v4l2_data.bufptr ) < 0 ) - { - Error( "Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno) ) - return( -1 ); - } - } else { - Error( "Unable to requeue buffer due to not v4l2_data" ) - } - } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - if ( channel_count > 1 ) - { - Debug( 3, "Switching video source" ); - int next_channel = (channel_index+1)%channel_count; - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(vid_src) ); - vid_src.channel = channel; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) + if ( v4l_version == 1 ) { - Error( "Failed to get camera source %d: %s", channel, strerror(errno) ); - return(-1); - } + if ( channel_count > 1 ) + { + Debug( 3, "Switching video source" ); + int next_channel = (channel_index+1)%channel_count; + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(vid_src) ); + vid_src.channel = channel; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) + { + Error( "Failed to get camera source %d: %s", channel, strerror(errno) ); + return(-1); + } - vid_src.channel = channels[next_channel]; - vid_src.norm = standards[next_channel]; - vid_src.flags = 0; - vid_src.type = VIDEO_TYPE_CAMERA; - if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) - { - Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); - return( -1 ); + vid_src.channel = channels[next_channel]; + vid_src.norm = standards[next_channel]; + vid_src.flags = 0; + vid_src.type = VIDEO_TYPE_CAMERA; + if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) + { + Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); + return( -1 ); + } + } + Debug( 3, "Requeueing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) + { + Error( "Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno) ); + return( -1 ); + } + v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames; } - } - Debug( 3, "Requeueing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) - { - Error( "Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno) ); - return( -1 ); - } - v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames; - } #endif // ZM_HAS_V4L1 - } - return( 0 ); + } + return( 0 ); } #endif // ZM_HAS_V4L diff --git a/src/zm_local_camera.h b/src/zm_local_camera.h index 335c06ce7..db9227cb4 100644 --- a/src/zm_local_camera.h +++ b/src/zm_local_camera.h @@ -47,99 +47,118 @@ class LocalCamera : public Camera { protected: #if ZM_HAS_V4L2 - struct V4L2MappedBuffer - { - void *start; - size_t length; - }; + struct V4L2MappedBuffer + { + void *start; + size_t length; + }; - struct V4L2Data - { - v4l2_cropcap cropcap; - v4l2_crop crop; - v4l2_format fmt; - v4l2_requestbuffers reqbufs; - V4L2MappedBuffer *buffers; - v4l2_buffer *bufptr; - }; + struct V4L2Data + { + v4l2_cropcap cropcap; + v4l2_crop crop; + v4l2_format fmt; + v4l2_requestbuffers reqbufs; + V4L2MappedBuffer *buffers; + v4l2_buffer *bufptr; + }; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - struct V4L1Data - { - int active_frame; - video_mbuf frames; - video_mmap *buffers; - unsigned char *bufptr; - }; + struct V4L1Data + { + int active_frame; + video_mbuf frames; + video_mmap *buffers; + unsigned char *bufptr; + }; #endif // ZM_HAS_V4L1 protected: - std::string device; - int channel; - int standard; - int palette; - bool device_prime; - bool channel_prime; - int channel_index; - unsigned int extras; - - unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ - convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ - - uint32_t AutoSelectFormat(int p_colours); + std::string device; + int channel; + int standard; + int palette; + bool device_prime; + bool channel_prime; + int channel_index; + unsigned int extras; + + unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ + convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ + + uint32_t AutoSelectFormat(int p_colours); - static int camera_count; - static int channel_count; - static int channels[VIDEO_MAX_FRAME]; - static int standards[VIDEO_MAX_FRAME]; - static int vid_fd; - static int v4l_version; - bool v4l_multi_buffer; - unsigned int v4l_captures_per_frame; + static int camera_count; + static int channel_count; + static int channels[VIDEO_MAX_FRAME]; + static int standards[VIDEO_MAX_FRAME]; + static int vid_fd; + static int v4l_version; + bool v4l_multi_buffer; + unsigned int v4l_captures_per_frame; #if ZM_HAS_V4L2 - static V4L2Data v4l2_data; + static V4L2Data v4l2_data; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - static V4L1Data v4l1_data; + static V4L1Data v4l1_data; #endif // ZM_HAS_V4L1 #if HAVE_LIBSWSCALE - static AVFrame **capturePictures; - _AVPIXELFORMAT imagePixFormat; - _AVPIXELFORMAT capturePixFormat; - struct SwsContext *imgConversionContext; - AVFrame *tmpPicture; + static AVFrame **capturePictures; + _AVPIXELFORMAT imagePixFormat; + _AVPIXELFORMAT capturePixFormat; + struct SwsContext *imgConversionContext; + AVFrame *tmpPicture; #endif // HAVE_LIBSWSCALE - static LocalCamera *last_camera; + static LocalCamera *last_camera; public: - LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0); - ~LocalCamera(); + LocalCamera( + int p_id, + const std::string &device, + int p_channel, + int p_format, + bool v4lmultibuffer, + unsigned int v4lcapturesperframe, + const std::string &p_method, + int p_width, + int p_height, + int p_colours, + int p_palette, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio, + unsigned int p_extras = 0); + ~LocalCamera(); - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - const std::string &Device() const { return( device ); } + const std::string &Device() const { return( device ); } - int Channel() const { return( channel ); } - int Standard() const { return( standard ); } - int Palette() const { return( palette ); } - int Extras() const { return( extras ); } + int Channel() const { return( channel ); } + int Standard() const { return( standard ); } + int Palette() const { return( palette ); } + int Extras() const { return( extras ); } - int Brightness( int p_brightness=-1 ); - int Hue( int p_hue=-1 ); - int Colour( int p_colour=-1 ); - int Contrast( int p_contrast=-1 ); + int Brightness( int p_brightness=-1 ); + int Hue( int p_hue=-1 ); + int Colour( int p_colour=-1 ); + int Contrast( int p_contrast=-1 ); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);}; - static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); + static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); }; #endif // ZM_HAS_V4L diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 4d66deee2..843dc7ac2 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -45,599 +45,599 @@ Logger::IntMap Logger::smSyslogPriorities; #if 0 static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 ) { - tp1->tv_sec -= tp2->tv_sec; - if ( tp1->tv_usec <= tp2->tv_usec ) - { - tp1->tv_sec--; - tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); - } - else - { - tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; - } + tp1->tv_sec -= tp2->tv_sec; + if ( tp1->tv_usec <= tp2->tv_usec ) + { + tp1->tv_sec--; + tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); + } + else + { + tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; + } } #endif void Logger::usrHandler( int sig ) { - Logger *logger = fetch(); - if ( sig == SIGUSR1 ) - logger->level( logger->level()+1 ); - else if ( sig == SIGUSR2 ) - logger->level( logger->level()-1 ); - Info( "Logger - Level changed to %d", logger->level() ); + Logger *logger = fetch(); + if ( sig == SIGUSR1 ) + logger->level( logger->level()+1 ); + else if ( sig == SIGUSR2 ) + logger->level( logger->level()-1 ); + Info( "Logger - Level changed to %d", logger->level() ); } Logger::Logger() : - mLevel( INFO ), - mTermLevel( NOLOG ), - mDatabaseLevel( NOLOG ), - mFileLevel( NOLOG ), - mSyslogLevel( NOLOG ), - mEffectiveLevel( NOLOG ), - //mLogPath( config.path_logs ), - //mLogFile( mLogPath+"/"+mId+".log" ), - mDbConnected( false ), - mLogFileFP( NULL ), - mHasTerm( false ), - mFlush( false ) + mLevel( INFO ), + mTermLevel( NOLOG ), + mDatabaseLevel( NOLOG ), + mFileLevel( NOLOG ), + mSyslogLevel( NOLOG ), + mEffectiveLevel( NOLOG ), + //mLogPath( config.path_logs ), + //mLogFile( mLogPath+"/"+mId+".log" ), + mDbConnected( false ), + mLogFileFP( NULL ), + mHasTerm( false ), + mFlush( false ) { - if ( smInstance ) - { - Panic( "Attempt to create second instance of Logger class" ); - } - - if ( !smInitialised ) - { - smCodes[INFO] = "INF"; - smCodes[WARNING] = "WAR"; - smCodes[ERROR] = "ERR"; - smCodes[FATAL] = "FAT"; - smCodes[PANIC] = "PNC"; - smCodes[NOLOG] = "OFF"; - - smSyslogPriorities[INFO] = LOG_INFO; - smSyslogPriorities[WARNING] = LOG_WARNING; - smSyslogPriorities[ERROR] = LOG_ERR; - smSyslogPriorities[FATAL] = LOG_ERR; - smSyslogPriorities[PANIC] = LOG_ERR; - - char code[4] = ""; - for ( int i = DEBUG1; i <= DEBUG9; i++ ) + if ( smInstance ) { - snprintf( code, sizeof(code), "DB%d", i ); - smCodes[i] = code; - smSyslogPriorities[i] = LOG_DEBUG; + Panic( "Attempt to create second instance of Logger class" ); } - smInitialised = true; - } + if ( !smInitialised ) + { + smCodes[INFO] = "INF"; + smCodes[WARNING] = "WAR"; + smCodes[ERROR] = "ERR"; + smCodes[FATAL] = "FAT"; + smCodes[PANIC] = "PNC"; + smCodes[NOLOG] = "OFF"; - if ( fileno(stderr) && isatty(fileno(stderr)) ) - mHasTerm = true; + smSyslogPriorities[INFO] = LOG_INFO; + smSyslogPriorities[WARNING] = LOG_WARNING; + smSyslogPriorities[ERROR] = LOG_ERR; + smSyslogPriorities[FATAL] = LOG_ERR; + smSyslogPriorities[PANIC] = LOG_ERR; + + char code[4] = ""; + for ( int i = DEBUG1; i <= DEBUG9; i++ ) + { + snprintf( code, sizeof(code), "DB%d", i ); + smCodes[i] = code; + smSyslogPriorities[i] = LOG_DEBUG; + } + + smInitialised = true; + } + + if ( fileno(stderr) && isatty(fileno(stderr)) ) + mHasTerm = true; } Logger::~Logger() { - terminate(); + terminate(); } void Logger::initialise( const std::string &id, const Options &options ) { - char *envPtr; + char *envPtr; - if ( !id.empty() ) - this->id( id ); + if ( !id.empty() ) + this->id( id ); - std::string tempLogFile; - if ( options.mLogPath.size() ) - { - mLogPath = options.mLogPath; - tempLogFile = mLogPath+"/"+mId+".log"; - } - if ( options.mLogFile.size() ) - tempLogFile = options.mLogFile; - else - tempLogFile = mLogPath+"/"+mId+".log"; - if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) - tempLogFile = envPtr; - - Level tempLevel = INFO; - Level tempTermLevel = mTermLevel; - Level tempDatabaseLevel = mDatabaseLevel; - Level tempFileLevel = mFileLevel; - Level tempSyslogLevel = mSyslogLevel; - - if ( options.mTermLevel != NOOPT ) - tempTermLevel = options.mTermLevel; - if ( options.mDatabaseLevel != NOOPT ) - tempDatabaseLevel = options.mDatabaseLevel; - else - tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database; - if ( options.mFileLevel != NOOPT ) - tempFileLevel = options.mFileLevel; - else - tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file; - if ( options.mSyslogLevel != NOOPT ) - tempSyslogLevel = options.mSyslogLevel; - else - tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; - - // Legacy - if ( (envPtr = getenv( "LOG_PRINT" )) ) - tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; - - if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) - tempLevel = atoi(envPtr); - - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) - tempTermLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) - tempDatabaseLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) - tempFileLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) - tempSyslogLevel = atoi(envPtr); - - if ( config.log_debug ) - { - StringVector targets = split( config.log_debug_target, "|" ); - for ( unsigned int i = 0; i < targets.size(); i++ ) + std::string tempLogFile; + if ( options.mLogPath.size() ) { - const std::string &target = targets[i]; - if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "_"+mIdRoot || target == "" ) - { - if ( config.log_debug_level > NOLOG ) + mLogPath = options.mLogPath; + tempLogFile = mLogPath+"/"+mId+".log"; + } + if ( options.mLogFile.size() ) + tempLogFile = options.mLogFile; + else + tempLogFile = mLogPath+"/"+mId+".log"; + if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) + tempLogFile = envPtr; + + Level tempLevel = INFO; + Level tempTermLevel = mTermLevel; + Level tempDatabaseLevel = mDatabaseLevel; + Level tempFileLevel = mFileLevel; + Level tempSyslogLevel = mSyslogLevel; + + if ( options.mTermLevel != NOOPT ) + tempTermLevel = options.mTermLevel; + if ( options.mDatabaseLevel != NOOPT ) + tempDatabaseLevel = options.mDatabaseLevel; + else + tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database; + if ( options.mFileLevel != NOOPT ) + tempFileLevel = options.mFileLevel; + else + tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file; + if ( options.mSyslogLevel != NOOPT ) + tempSyslogLevel = options.mSyslogLevel; + else + tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; + + // Legacy + if ( (envPtr = getenv( "LOG_PRINT" )) ) + tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; + + if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) + tempLevel = atoi(envPtr); + + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) + tempTermLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) + tempDatabaseLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) + tempFileLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) + tempSyslogLevel = atoi(envPtr); + + if ( config.log_debug ) + { + StringVector targets = split( config.log_debug_target, "|" ); + for ( unsigned int i = 0; i < targets.size(); i++ ) { - tempLevel = config.log_debug_level; - if ( config.log_debug_file[0] ) - { - tempLogFile = config.log_debug_file; - tempFileLevel = tempLevel; - } + const std::string &target = targets[i]; + if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "_"+mIdRoot || target == "" ) + { + if ( config.log_debug_level > NOLOG ) + { + tempLevel = config.log_debug_level; + if ( config.log_debug_file[0] ) + { + tempLogFile = config.log_debug_file; + tempFileLevel = tempLevel; + } + } + } } - } } - } - logFile( tempLogFile ); + logFile( tempLogFile ); - termLevel( tempTermLevel ); - databaseLevel( tempDatabaseLevel ); - fileLevel( tempFileLevel ); - syslogLevel( tempSyslogLevel ); + termLevel( tempTermLevel ); + databaseLevel( tempDatabaseLevel ); + fileLevel( tempFileLevel ); + syslogLevel( tempSyslogLevel ); - level( tempLevel ); + level( tempLevel ); - mFlush = (envPtr = getenv( "LOG_FLUSH")) ? atoi( envPtr ) : false; + mFlush = (envPtr = getenv( "LOG_FLUSH")) ? atoi( envPtr ) : false; - //mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false; + //mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false; - { - struct sigaction action; - memset( &action, 0, sizeof(action) ); - action.sa_handler = usrHandler; - action.sa_flags = SA_RESTART; - - if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) { - Fatal( "sigaction(), error = %s", strerror(errno) ); - } - if ( sigaction( SIGUSR2, &action, 0 ) < 0) - { - Fatal( "sigaction(), error = %s", strerror(errno) ); - } - } + struct sigaction action; + memset( &action, 0, sizeof(action) ); + action.sa_handler = usrHandler; + action.sa_flags = SA_RESTART; - mInitialised = true; + if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) + { + Fatal( "sigaction(), error = %s", strerror(errno) ); + } + if ( sigaction( SIGUSR2, &action, 0 ) < 0) + { + Fatal( "sigaction(), error = %s", strerror(errno) ); + } + } - Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", smCodes[mLevel].c_str(), smCodes[mEffectiveLevel].c_str(), smCodes[mTermLevel].c_str(), smCodes[mDatabaseLevel].c_str(), smCodes[mFileLevel].c_str(), mLogFile.c_str(), smCodes[mSyslogLevel].c_str() ); + mInitialised = true; + + Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", smCodes[mLevel].c_str(), smCodes[mEffectiveLevel].c_str(), smCodes[mTermLevel].c_str(), smCodes[mDatabaseLevel].c_str(), smCodes[mFileLevel].c_str(), mLogFile.c_str(), smCodes[mSyslogLevel].c_str() ); } void Logger::terminate() { - Debug(1, "Terminating Logger" ); + Debug(1, "Terminating Logger" ); - if ( mFileLevel > NOLOG ) - closeFile(); + if ( mFileLevel > NOLOG ) + closeFile(); - if ( mSyslogLevel > NOLOG ) - closeSyslog(); + if ( mSyslogLevel > NOLOG ) + closeSyslog(); - if ( mDatabaseLevel > NOLOG ) - closeDatabase(); + if ( mDatabaseLevel > NOLOG ) + closeDatabase(); } bool Logger::boolEnv( const std::string &name, bool defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? atoi( envPtr ) : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? atoi( envPtr ) : defaultValue ); } int Logger::intEnv( const std::string &name, bool defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? atoi( envPtr ) : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? atoi( envPtr ) : defaultValue ); } std::string Logger::strEnv( const std::string &name, const std::string defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? envPtr : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? envPtr : defaultValue ); } char *Logger::getTargettedEnv( const std::string &name ) { - char *envPtr = NULL; - std::string envName; + char *envPtr = NULL; + std::string envName; - envName = name+"_"+mId; - envPtr = getenv( envName.c_str() ); - if ( !envPtr && mId != mIdRoot ) - { - envName = name+"_"+mIdRoot; + envName = name+"_"+mId; envPtr = getenv( envName.c_str() ); - } - if ( !envPtr ) - envPtr = getenv( name.c_str() ); - return( envPtr ); + if ( !envPtr && mId != mIdRoot ) + { + envName = name+"_"+mIdRoot; + envPtr = getenv( envName.c_str() ); + } + if ( !envPtr ) + envPtr = getenv( name.c_str() ); + return( envPtr ); } const std::string &Logger::id( const std::string &id ) { - std::string tempId = id; + std::string tempId = id; - size_t pos; - // Remove whitespace - while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) - { - tempId.replace( pos, 1, "" ); - } - // Replace non-alphanum with underscore - while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) - { - tempId.replace( pos, 1, "_" ); - } - if ( mId != tempId ) - { - mId = tempId; - pos = mId.find( '_' ); - if ( pos != std::string::npos ) + size_t pos; + // Remove whitespace + while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) { - mIdRoot = mId.substr( 0, pos ); - if ( ++pos < mId.size() ) - mIdArgs = mId.substr( pos ); + tempId.replace( pos, 1, "" ); } - } - return( mId ); + // Replace non-alphanum with underscore + while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) + { + tempId.replace( pos, 1, "_" ); + } + if ( mId != tempId ) + { + mId = tempId; + pos = mId.find( '_' ); + if ( pos != std::string::npos ) + { + mIdRoot = mId.substr( 0, pos ); + if ( ++pos < mId.size() ) + mIdArgs = mId.substr( pos ); + } + } + return( mId ); } Logger::Level Logger::level( Logger::Level level ) { - if ( level > NOOPT ) - { - level = limit(level); - if ( mLevel != level ) - mLevel = level; + if ( level > NOOPT ) + { + level = limit(level); + if ( mLevel != level ) + mLevel = level; - mEffectiveLevel = NOLOG; - if ( mTermLevel > mEffectiveLevel ) - mEffectiveLevel = mTermLevel; - if ( mDatabaseLevel > mEffectiveLevel ) - mEffectiveLevel = mDatabaseLevel; - if ( mFileLevel > mEffectiveLevel ) - mEffectiveLevel = mFileLevel; - if ( mSyslogLevel > mEffectiveLevel ) - mEffectiveLevel = mSyslogLevel; - if ( mEffectiveLevel > mLevel) - mEffectiveLevel = mLevel; - } - return( mLevel ); + mEffectiveLevel = NOLOG; + if ( mTermLevel > mEffectiveLevel ) + mEffectiveLevel = mTermLevel; + if ( mDatabaseLevel > mEffectiveLevel ) + mEffectiveLevel = mDatabaseLevel; + if ( mFileLevel > mEffectiveLevel ) + mEffectiveLevel = mFileLevel; + if ( mSyslogLevel > mEffectiveLevel ) + mEffectiveLevel = mSyslogLevel; + if ( mEffectiveLevel > mLevel) + mEffectiveLevel = mLevel; + } + return( mLevel ); } Logger::Level Logger::termLevel( Logger::Level termLevel ) { - if ( termLevel > NOOPT ) - { - if ( !mHasTerm ) - termLevel = NOLOG; - termLevel = limit(termLevel); - if ( mTermLevel != termLevel ) - mTermLevel = termLevel; - } - return( mTermLevel ); + if ( termLevel > NOOPT ) + { + if ( !mHasTerm ) + termLevel = NOLOG; + termLevel = limit(termLevel); + if ( mTermLevel != termLevel ) + mTermLevel = termLevel; + } + return( mTermLevel ); } Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) { - if ( databaseLevel > NOOPT ) - { - databaseLevel = limit(databaseLevel); - if ( mDatabaseLevel != databaseLevel ) + if ( databaseLevel > NOOPT ) { - if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) - { - if ( !mDbConnected ) + databaseLevel = limit(databaseLevel); + if ( mDatabaseLevel != databaseLevel ) { - if ( !mysql_init( &mDbConnection ) ) - { - Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - my_bool reconnect = 1; - if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); - std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); - if ( colonIndex != std::string::npos ) - { - std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); - std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); - if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) + if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) { - Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); + if ( !mDbConnected ) + { + if ( !mysql_init( &mDbConnection ) ) + { + Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + my_bool reconnect = 1; + if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) + Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); + std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); + if ( colonIndex != std::string::npos ) + { + std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); + std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); + if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) + { + Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + } + else + { + if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) + { + Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + } + unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection ); + if ( mysqlVersion < 50019 ) + if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) + Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); + if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) + { + Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + mDbConnected = true; + } } - } - else - { - if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) - { - Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - } - unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection ); - if ( mysqlVersion < 50019 ) - if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); - if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) - { - Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - mDbConnected = true; + mDatabaseLevel = databaseLevel; } - } - mDatabaseLevel = databaseLevel; } - } - return( mDatabaseLevel ); + return( mDatabaseLevel ); } Logger::Level Logger::fileLevel( Logger::Level fileLevel ) { - if ( fileLevel > NOOPT ) - { - fileLevel = limit(fileLevel); - if ( mFileLevel != fileLevel ) + if ( fileLevel > NOOPT ) { - if ( mFileLevel > NOLOG ) - closeFile(); - mFileLevel = fileLevel; - if ( mFileLevel > NOLOG ) - openFile(); + fileLevel = limit(fileLevel); + if ( mFileLevel != fileLevel ) + { + if ( mFileLevel > NOLOG ) + closeFile(); + mFileLevel = fileLevel; + if ( mFileLevel > NOLOG ) + openFile(); + } } - } - return( mFileLevel ); + return( mFileLevel ); } Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) { - if ( syslogLevel > NOOPT ) - { - syslogLevel = limit(syslogLevel); - if ( mSyslogLevel != syslogLevel ) + if ( syslogLevel > NOOPT ) { - if ( mSyslogLevel > NOLOG ) - closeSyslog(); - mSyslogLevel = syslogLevel; - if ( mSyslogLevel > NOLOG ) - openSyslog(); + syslogLevel = limit(syslogLevel); + if ( mSyslogLevel != syslogLevel ) + { + if ( mSyslogLevel > NOLOG ) + closeSyslog(); + mSyslogLevel = syslogLevel; + if ( mSyslogLevel > NOLOG ) + openSyslog(); + } } - } - return( mSyslogLevel ); + return( mSyslogLevel ); } void Logger::logFile( const std::string &logFile ) { - bool addLogPid = false; - std::string tempLogFile = logFile; - if ( tempLogFile[tempLogFile.length()-1] == '+' ) - { - tempLogFile.resize(tempLogFile.length()-1); - addLogPid = true; - } - if ( addLogPid ) - mLogFile = stringtf( "%s.%05d", tempLogFile.c_str(), getpid() ); - else - mLogFile = tempLogFile; + bool addLogPid = false; + std::string tempLogFile = logFile; + if ( tempLogFile[tempLogFile.length()-1] == '+' ) + { + tempLogFile.resize(tempLogFile.length()-1); + addLogPid = true; + } + if ( addLogPid ) + mLogFile = stringtf( "%s.%05d", tempLogFile.c_str(), getpid() ); + else + mLogFile = tempLogFile; } void Logger::openFile() { - if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"w" )) == (FILE *)NULL ) - { - mFileLevel = NOLOG; - Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) ); - } + if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"w" )) == (FILE *)NULL ) + { + mFileLevel = NOLOG; + Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) ); + } } void Logger::closeFile() { - if ( mLogFileFP ) - { - fflush( mLogFileFP ); - if ( fclose( mLogFileFP ) < 0 ) + if ( mLogFileFP ) { - Fatal( "fclose(), error = %s",strerror(errno) ); + fflush( mLogFileFP ); + if ( fclose( mLogFileFP ) < 0 ) + { + Fatal( "fclose(), error = %s",strerror(errno) ); + } + mLogFileFP = (FILE *)NULL; } - mLogFileFP = (FILE *)NULL; - } } void Logger::closeDatabase() { - if ( mDbConnected ) - { - mysql_close( &mDbConnection ); - mDbConnected = false; - } + if ( mDbConnected ) + { + mysql_close( &mDbConnection ); + mDbConnected = false; + } } void Logger::openSyslog() { - (void) openlog( mId.c_str(), LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); + (void) openlog( mId.c_str(), LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); } void Logger::closeSyslog() { - (void) closelog(); + (void) closelog(); } void Logger::logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ) { - if ( level <= mEffectiveLevel ) - { - char classString[4]; - char timeString[64]; - char logString[8192]; - va_list argPtr; - struct timeval timeVal; - - const char * const file = basename(filepath); - - if ( level < PANIC || level > DEBUG9 ) - Panic( "Invalid logger level %d", level ); - - strncpy( classString, smCodes[level].c_str(), sizeof(classString) ); - - gettimeofday( &timeVal, NULL ); - - #if 0 - if ( logRuntime ) + if ( level <= mEffectiveLevel ) { - static struct timeval logStart; + char classString[4]; + char timeString[64]; + char logString[8192]; + va_list argPtr; + struct timeval timeVal; - subtractTime( &timeVal, &logStart ); + const char * const file = basename(filepath); + + if ( level < PANIC || level > DEBUG9 ) + Panic( "Invalid logger level %d", level ); - 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 ); - #if 0 - } - #endif + strncpy( classString, smCodes[level].c_str(), sizeof(classString) ); - pid_t tid; + gettimeofday( &timeVal, NULL ); + + #if 0 + if ( logRuntime ) + { + static struct timeval logStart; + + subtractTime( &timeVal, &logStart ); + + 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 ); + #if 0 + } + #endif + + 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 + #ifdef __FreeBSD_kernel__ + if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id - # else - // SOLARIS doesn't have SYS_gettid; don't assume - #ifdef SYS_gettid - if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id - #endif // SYS_gettid - #endif + # else + // SOLARIS doesn't have SYS_gettid; don't assume + #ifdef SYS_gettid + 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_end(argPtr); - char *syslogEnd = logPtr; - strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) ); + 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_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 ( mFlush ) - fflush( mLogFileFP ); - } - *syslogEnd = '\0'; - if ( level <= mDatabaseLevel ) - { - char sql[ZM_SQL_MED_BUFSIZ]; - char escapedString[(strlen(syslogStart)*2)+1]; + if ( level <= mTermLevel ) + { + printf( "%s", logString ); + fflush( stdout ); + } + if ( level <= mFileLevel ) + { + fprintf( mLogFileFP, "%s", logString ); + //if ( mFlush ) + fflush( mLogFileFP ); + } + *syslogEnd = '\0'; + if ( level <= mDatabaseLevel ) + { + char sql[ZM_SQL_MED_BUFSIZ]; + char escapedString[(strlen(syslogStart)*2)+1]; - mysql_real_escape_string( &mDbConnection, 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( &mDbConnection, sql ) ) - { - databaseLevel( NOLOG ); - Error( "Can't insert log entry: %s", mysql_error( &mDbConnection ) ); - } - } - if ( level <= mSyslogLevel ) - { - int priority = smSyslogPriorities[level]; - //priority |= LOG_DAEMON; - syslog( priority, "%s [%s]", classString, syslogStart ); - } + mysql_real_escape_string( &mDbConnection, 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( &mDbConnection, sql ) ) + { + databaseLevel( NOLOG ); + Error( "Can't insert log entry: %s", mysql_error( &mDbConnection ) ); + } + } + if ( level <= mSyslogLevel ) + { + int priority = smSyslogPriorities[level]; + //priority |= LOG_DAEMON; + syslog( priority, "%s [%s] [%s]", classString, mId.c_str(), syslogStart ); + } - if ( level <= FATAL ) - { - if ( level <= PANIC ) - abort(); - exit( -1 ); + if ( level <= FATAL ) + { + if ( level <= PANIC ) + abort(); + exit( -1 ); + } } - } } void logInit( const char *name, const Logger::Options &options ) { - if ( !Logger::smInstance ) - Logger::smInstance = new Logger(); - Logger::Options tempOptions = options; - tempOptions.mLogPath = config.path_logs; - Logger::smInstance->initialise( name, tempOptions ); + if ( !Logger::smInstance ) + Logger::smInstance = new Logger(); + Logger::Options tempOptions = options; + tempOptions.mLogPath = config.path_logs; + Logger::smInstance->initialise( name, tempOptions ); } void logTerm() { - if ( Logger::smInstance ) - delete Logger::smInstance; + if ( Logger::smInstance ) + delete Logger::smInstance; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 3f381ee15..46bc79a18 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -28,6 +28,7 @@ #include "zm_mpeg.h" #include "zm_signal.h" #include "zm_monitor.h" +#include "zm_video.h" #if ZM_HAS_V4L #include "zm_local_camera.h" #endif // ZM_HAS_V4L @@ -74,8 +75,7 @@ std::vector split(const std::string &s, char delim) { -Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) -{ +Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) { strncpy( name, p_name, sizeof(name) ); #if ZM_MEM_MAPPED @@ -94,15 +94,12 @@ Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) connected = false; } -Monitor::MonitorLink::~MonitorLink() -{ +Monitor::MonitorLink::~MonitorLink() { disconnect(); } -bool Monitor::MonitorLink::connect() -{ - if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) - { +bool Monitor::MonitorLink::connect() { + if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) { last_connect_time = time( 0 ); mem_size = sizeof(SharedData) + sizeof(TriggerData); @@ -110,8 +107,7 @@ bool Monitor::MonitorLink::connect() Debug( 1, "link.mem.size=%d", mem_size ); #if ZM_MEM_MAPPED map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); - if ( map_fd < 0 ) - { + if ( map_fd < 0 ) { Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); disconnect(); return( false ); @@ -124,44 +120,37 @@ bool Monitor::MonitorLink::connect() } struct stat map_stat; - if ( fstat( map_fd, &map_stat ) < 0 ) - { + if ( fstat( map_fd, &map_stat ) < 0 ) { Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); disconnect(); return( false ); } - if ( map_stat.st_size == 0 ) - { + if ( map_stat.st_size == 0 ) { Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); disconnect(); return( false ); - } - else if ( map_stat.st_size < mem_size ) - { + } else if ( map_stat.st_size < mem_size ) { Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); disconnect(); return( false ); } mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); - if ( mem_ptr == MAP_FAILED ) - { + if ( mem_ptr == MAP_FAILED ) { Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); disconnect(); return( false ); } #else // ZM_MEM_MAPPED shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); - if ( shm_id < 0 ) - { + if ( shm_id < 0 ) { Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); connected = false; return( false ); } mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); - if ( mem_ptr < 0 ) - { + if ( mem_ptr < 0 ) { Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); connected = false; return( false ); @@ -171,8 +160,7 @@ bool Monitor::MonitorLink::connect() shared_data = (SharedData *)mem_ptr; trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - if ( !shared_data->valid ) - { + if ( !shared_data->valid ) { Debug( 3, "Linked memory not initialised by capture daemon" ); disconnect(); return( false ); @@ -187,15 +175,12 @@ bool Monitor::MonitorLink::connect() return( false ); } -bool Monitor::MonitorLink::disconnect() -{ - if ( connected ) - { +bool Monitor::MonitorLink::disconnect() { + if ( connected ) { connected = false; #if ZM_MEM_MAPPED - if ( mem_ptr > 0 ) - { + if ( mem_ptr > 0 ) { msync( mem_ptr, mem_size, MS_ASYNC ); munmap( mem_ptr, mem_size ); } @@ -205,25 +190,21 @@ bool Monitor::MonitorLink::disconnect() map_fd = -1; #else // ZM_MEM_MAPPED struct shmid_ds shm_data; - if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) - { + if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { Debug( 3, "Can't shmctl: %s", strerror(errno) ); return( false ); } shm_id = 0; - if ( shm_data.shm_nattch <= 1 ) - { - if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) - { + if ( shm_data.shm_nattch <= 1 ) { + if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { Debug( 3, "Can't shmctl: %s", strerror(errno) ); return( false ); } } - if ( shmdt( mem_ptr ) < 0 ) - { + if ( shmdt( mem_ptr ) < 0 ) { Debug( 3, "Can't shmdt: %s", strerror(errno) ); return( false ); } @@ -235,19 +216,15 @@ bool Monitor::MonitorLink::disconnect() return( true ); } -bool Monitor::MonitorLink::isAlarmed() -{ - if ( !connected ) - { +bool Monitor::MonitorLink::isAlarmed() { + if ( !connected ) { return( false ); } return( shared_data->state == ALARM ); } -bool Monitor::MonitorLink::inAlarm() -{ - if ( !connected ) - { +bool Monitor::MonitorLink::inAlarm() { + if ( !connected ) { return( false ); } return( shared_data->state == ALARM || shared_data->state == ALERT ); @@ -255,105 +232,29 @@ bool Monitor::MonitorLink::inAlarm() bool Monitor::MonitorLink::hasAlarmed() { - if ( shared_data->state == ALARM ) - { + if ( shared_data->state == ALARM ) { return( true ); - } - else if( shared_data->last_event != (unsigned int)last_event ) - { + } else if ( shared_data->last_event != (unsigned int)last_event ) { last_event = shared_data->last_event; } return( false ); } Monitor::Monitor( -<<<<<<< HEAD - int p_id, - const char *p_name, - const unsigned int p_server_id, - const unsigned int p_storage_id, - int p_function, - bool p_enabled, - const char *p_linked_monitors, - Camera *p_camera, - int p_orientation, - unsigned int p_deinterlacing, - const char *p_event_prefix, - const char *p_label_format, - const Coord &p_label_coord, - int p_label_size, - int p_image_buffer_count, - int p_warmup_count, - int p_pre_event_count, - int p_post_event_count, - int p_stream_replay_buffer, - int p_alarm_frame_count, - int p_section_length, - int p_frame_skip, - int p_motion_frame_skip, - double p_analysis_fps, - unsigned int p_analysis_update_delay, - int p_capture_delay, - int p_alarm_capture_delay, - int p_fps_report_interval, - int p_ref_blend_perc, - int p_alarm_ref_blend_perc, - bool p_track_motion, - Rgb p_signal_check_colour, - bool p_embed_exif, - Purpose p_purpose, - int p_n_zones, - Zone *p_zones[] -) : id( p_id ), - server_id( p_server_id ), - storage_id( p_storage_id ), - function( (Function)p_function ), - enabled( p_enabled ), - width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), - height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ), - orientation( (Orientation)p_orientation ), - deinterlacing( p_deinterlacing ), - label_coord( p_label_coord ), - label_size( p_label_size ), - image_buffer_count( p_image_buffer_count ), - warmup_count( p_warmup_count ), - pre_event_count( p_pre_event_count ), - post_event_count( p_post_event_count ), - stream_replay_buffer( p_stream_replay_buffer ), - section_length( p_section_length ), - frame_skip( p_frame_skip ), - motion_frame_skip( p_motion_frame_skip ), - analysis_fps( p_analysis_fps ), - analysis_update_delay( p_analysis_update_delay ), - capture_delay( p_capture_delay ), - alarm_capture_delay( p_alarm_capture_delay ), - alarm_frame_count( p_alarm_frame_count ), - fps_report_interval( p_fps_report_interval ), - ref_blend_perc( p_ref_blend_perc ), - alarm_ref_blend_perc( p_alarm_ref_blend_perc ), - track_motion( p_track_motion ), - signal_check_colour( p_signal_check_colour ), - embed_exif( p_embed_exif ), - delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), - ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), - purpose( p_purpose ), - last_motion_score(0), - camera( p_camera ), - n_zones( p_n_zones ), - zones( p_zones ), - timestamps( 0 ), - images( 0 ), - privacy_bitmask( NULL ) -======= int p_id, const char *p_name, const unsigned int p_server_id, + const unsigned int p_storage_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, + int p_savejpegs, + int p_videowriter, + std::string p_encoderparams, + bool p_record_audio, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, @@ -382,12 +283,17 @@ Monitor::Monitor( Zone *p_zones[] ) : id( p_id ), server_id( p_server_id ), + storage_id( p_storage_id ), function( (Function)p_function ), enabled( p_enabled ), width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ), orientation( (Orientation)p_orientation ), deinterlacing( p_deinterlacing ), + savejpegspref( p_savejpegs ), + videowriterpref( p_videowriter ), + encoderparams( p_encoderparams ), + record_audio( p_record_audio ), label_coord( p_label_coord ), label_size( p_label_size ), image_buffer_count( p_image_buffer_count ), @@ -419,7 +325,6 @@ Monitor::Monitor( timestamps( 0 ), images( 0 ), privacy_bitmask( NULL ) ->>>>>>> master { strncpy( name, p_name, sizeof(name)-1 ); @@ -444,6 +349,9 @@ Monitor::Monitor( } } + /* Parse encoder parameters */ + ParseEncoderParameters(encoderparams.c_str(), &encoderparamsvec); + fps = 0.0; event_count = 0; image_count = 0; @@ -470,6 +378,7 @@ Monitor::Monitor( mem_size = sizeof(SharedData) + sizeof(TriggerData) + + sizeof(VideoStoreData) //Information to pass back to the capture process + (image_buffer_count*sizeof(struct timeval)) + (image_buffer_count*camera->ImageSize()) + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ @@ -505,6 +414,10 @@ Monitor::Monitor( trigger_data->trigger_text[0] = 0; trigger_data->trigger_showtext[0] = 0; shared_data->valid = true; + video_store_data->recording = false; + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "nothing"); + video_store_data->size = sizeof(VideoStoreData); + //video_store_data->frameNumber = 0; } else if ( purpose == ANALYSIS ) { this->connect(); if ( ! mem_ptr ) exit(-1); @@ -514,16 +427,15 @@ Monitor::Monitor( shared_data->alarm_y = -1; } - if ( ( ! mem_ptr ) || ! shared_data->valid ) - { - if ( purpose != QUERY ) - { + if ( ( ! mem_ptr ) || ! shared_data->valid ) { + if ( purpose != QUERY ) { Error( "Shared data not initialised by capture daemon for monitor %s", name ); exit( -1 ); } } // Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. + // In my storage areas branch, I took this out.. and didn't notice any problems. if ( !n_zones ) { Debug( 1, "Monitor %s has no zones, adding one.", name ); n_zones = 1; @@ -535,74 +447,39 @@ Monitor::Monitor( event = 0; -<<<<<<< HEAD - storage = new Storage( storage_id ); - Debug(1, "Storage path: %s", storage->Path() ); - - if ( purpose == ANALYSIS ) - { - static char path[PATH_MAX]; - - strncpy( path, storage->Path(), sizeof(path) ); -======= Debug( 1, "Monitor %s has function %d", name, function ); Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d, LBS = %d", name, label_format, label_coord.X(), label_coord.Y(), label_size ); Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); - if ( purpose == ANALYSIS ) - { + storage = new Storage( storage_id ); + Debug(1, "Storage path: %s", storage->Path() ); + + if ( purpose == ANALYSIS ) { static char path[PATH_MAX]; ->>>>>>> master - strncpy( path, config.dir_events, sizeof(path) ); + strncpy( path, storage->Path(), sizeof(path) ); -<<<<<<< HEAD - snprintf( path, sizeof(path), "%s/%d", storage->Path(), id ); - - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't make %s: %s", path, strerror(errno)); - } - char temp_path[PATH_MAX]; - snprintf( temp_path, sizeof(temp_path), "%d", id ); - if ( chdir( storage->Path() ) < 0 ) - Fatal( "Can't change directory to '%s': %s", storage->Path(), strerror(errno) ); - if ( symlink( temp_path, name ) < 0 ) - Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); - if ( chdir( ".." ) < 0 ) - Fatal( "Can't change to parent directory: %s", strerror(errno) ); - } -======= struct stat statbuf; errno = 0; stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { + if ( errno == ENOENT || errno == ENOTDIR ) { + if ( mkdir( path, 0755 ) ) { Error( "Can't make %s: %s", path, strerror(errno)); } } - snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); ->>>>>>> master + snprintf( path, sizeof(path), "%s/%d", storage->Path(), id ); errno = 0; stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { + if ( errno == ENOENT || errno == ENOTDIR ) { + if ( mkdir( path, 0755 ) ) { Error( "Can't make %s: %s", path, strerror(errno)); } char temp_path[PATH_MAX]; snprintf( temp_path, sizeof(temp_path), "%d", id ); - if ( chdir( config.dir_events ) < 0 ) - Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); + if ( chdir( storage->Path() ) < 0 ) + Fatal( "Can't change directory to '%s': %s", storage->Path(), strerror(errno) ); if ( symlink( temp_path, name ) < 0 ) Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); if ( chdir( ".." ) < 0 ) @@ -610,8 +487,7 @@ Monitor::Monitor( } while( shared_data->last_write_index == (unsigned int)image_buffer_count - && shared_data->last_write_time == 0) - { + && shared_data->last_write_time == 0) { Warning( "Waiting for capture daemon" ); sleep( 1 ); } @@ -670,15 +546,15 @@ bool Monitor::connect() { exit( -1 ); } mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); - if ( mem_ptr < 0 ) - { + if ( mem_ptr < 0 ) { Error( "Can't shmat: %s", strerror(errno)); exit( -1 ); } #endif // ZM_MEM_MAPPED shared_data = (SharedData *)mem_ptr; trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); + 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 % 16) != 0) { @@ -687,29 +563,25 @@ bool Monitor::connect() { shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); } image_buffer = new Snapshot[image_buffer_count]; - for ( int i = 0; i < image_buffer_count; i++ ) - { + 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) - { + 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 ) - { + 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 // alarmed images that must be discarded when event is created pre_event_buffer_count = pre_event_count + alarm_frame_count - 1; pre_event_buffer = new Snapshot[pre_event_buffer_count]; - for ( int i = 0; i < pre_event_buffer_count; i++ ) - { + for ( int i = 0; i < pre_event_buffer_count; i++ ) { pre_event_buffer[i].timestamp = new struct timeval; pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); } @@ -728,10 +600,10 @@ Monitor::~Monitor() delete[] images; images = 0; } - if ( privacy_bitmask ) { - delete[] privacy_bitmask; - privacy_bitmask = NULL; - } + if ( privacy_bitmask ) { + delete[] privacy_bitmask; + privacy_bitmask = NULL; + } if ( mem_ptr ) { if ( event ) Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); @@ -756,6 +628,7 @@ Monitor::~Monitor() delete[] zones; delete camera; + delete storage; if ( mem_ptr ) { if ( purpose == ANALYSIS ) @@ -779,36 +652,6 @@ Monitor::~Monitor() shared_data->valid = false; memset( mem_ptr, 0, mem_size ); } -<<<<<<< HEAD - delete[] zones; - - delete camera; - delete storage; - - if ( mem_ptr ) { - if ( purpose == ANALYSIS ) - { - shared_data->state = state = IDLE; - shared_data->last_read_index = image_buffer_count; - shared_data->last_read_time = 0; - - if ( analysis_fps ) - { - for ( int i = 0; i < pre_event_buffer_count; i++ ) - { - delete pre_event_buffer[i].image; - delete pre_event_buffer[i].timestamp; - } - delete[] pre_event_buffer; - } - } - else if ( purpose == CAPTURE ) - { - shared_data->valid = false; - memset( mem_ptr, 0, mem_size ); - } -======= ->>>>>>> master #if ZM_MEM_MAPPED if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) @@ -1253,8 +1096,7 @@ int Monitor::actionColour( int p_colour ) return( camera->Colour( p_colour ) ); } -void Monitor::DumpZoneImage( const char *zone_string ) -{ +void Monitor::DumpZoneImage( const char *zone_string ) { int exclude_id = 0; int extra_colour = 0; Polygon extra_zone; @@ -1267,13 +1109,32 @@ void Monitor::DumpZoneImage( const char *zone_string ) } } - int index = shared_data->last_write_index; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + Image *zone_image = NULL; + if ( ( (!staticConfig.SERVER_ID) || ( staticConfig.SERVER_ID == server_id ) ) && mem_ptr ) { + Debug(3, "Trying to load from local zmc"); + int index = shared_data->last_write_index; + Snapshot *snap = &image_buffer[index]; + zone_image = new Image( *snap->image ); + } else { + Debug(3, "Trying to load from event"); + // Grab the most revent event image + std::string sql = stringtf( "SELECT MAX(Id) FROM Events WHERE MonitorId=%d AND Frames > 0", id ); + zmDbRow eventid_row; + if ( eventid_row.fetch( sql.c_str() ) ) { + int event_id = atoi( eventid_row[0] ); - Image zone_image( *snap_image ); - if(zone_image.Colours() == ZM_COLOUR_GRAY8) { - zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + Debug( 3, "Got event %d", event_id ); + EventStream *stream = new EventStream(); + stream->setStreamStart( event_id, (unsigned int)1 ); + zone_image = stream->getImage(); + } else { + Error("Unable to load an event for monitor %d", id ); + return; + } + } + + if(zone_image->Colours() == ZM_COLOUR_GRAY8) { + zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); } for( int i = 0; i < n_zones; i++ ) @@ -1309,19 +1170,20 @@ void Monitor::DumpZoneImage( const char *zone_string ) colour = RGB_WHITE; } } - zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); - zone_image.Outline( colour, zones[i]->GetPolygon() ); + zone_image->Fill( colour, 2, zones[i]->GetPolygon() ); + zone_image->Outline( colour, zones[i]->GetPolygon() ); } if ( extra_zone.getNumCoords() ) { - zone_image.Fill( extra_colour, 2, extra_zone ); - zone_image.Outline( extra_colour, extra_zone ); + zone_image->Fill( extra_colour, 2, extra_zone ); + zone_image->Outline( extra_colour, extra_zone ); } static char filename[PATH_MAX]; snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); - zone_image.WriteJpeg( filename ); + zone_image->WriteJpeg( filename ); + delete zone_image; } void Monitor::DumpImage( Image *dump_image ) const @@ -1417,6 +1279,7 @@ bool Monitor::Analyse() { if ( shared_data->last_read_index == shared_data->last_write_index ) { + // I wonder how often this happens. Maybe if this happens we should sleep or something? return( false ); } @@ -1437,8 +1300,10 @@ bool Monitor::Analyse() if ( read_margin < 0 ) read_margin += image_buffer_count; int step = 1; + // Isn't read_margin always > 0 here? if ( read_margin > 0 ) { + // TODO explain this so... 90% of image buffer / 50% of read margin? step = (9*image_buffer_count)/(5*read_margin); } @@ -1518,6 +1383,7 @@ bool Monitor::Analyse() if ( static_undef ) { +// Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary. static_undef = false; timestamps = new struct timeval *[pre_event_count]; images = new Image *[pre_event_count]; @@ -1528,6 +1394,11 @@ bool Monitor::Analyse() { bool signal = shared_data->signal; bool signal_change = (signal != last_signal); + + //Set video recording flag for event start constructor and easy reference in code + // TODO: Use enum instead of the # 2. Makes for easier reading + bool videoRecording = ((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()); + if ( trigger_data->trigger_state != TRIGGER_OFF ) { unsigned int score = 0; @@ -1639,10 +1510,15 @@ bool Monitor::Analyse() if ( noteSet.size() > 0 ) noteSetMap[LINKED_CAUSE] = noteSet; } + + //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { if ( event ) { + //TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here?? + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); + int section_mod = timestamp->tv_sec%section_length; if ( section_mod < last_section_mod ) { @@ -1668,9 +1544,12 @@ bool Monitor::Analyse() { // Create event - event = new Event( this, *timestamp, "Continuous", noteSetMap ); + event = new Event( this, *timestamp, "Continuous", noteSetMap, videoRecording ); shared_data->last_event = event->Id(); - + //set up video store data + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); + video_store_data->recording = true; + Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); /* To prevent cancelling out an existing alert\prealarm\alarm state */ @@ -1789,6 +1668,9 @@ bool Monitor::Analyse() event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); } shared_data->last_event = event->Id(); + //set up video store data + snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); + video_store_data->recording = true; Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); @@ -1935,6 +1817,11 @@ bool Monitor::Analyse() } else if ( state == TAPE ) { + //Video Storage: activate only for supported cameras. Event::AddFrame knows whether or not we are recording video and saves frames accordingly + if((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()) + { + video_store_data->recording = true; + } if ( !(image_count%(frame_skip+1)) ) { if ( config.bulk_frame_interval > 1 ) @@ -1998,6 +1885,7 @@ void Monitor::Reload() closeEvent(); static char sql[ZM_SQL_MED_BUFSIZ]; + // This seems to have fallen out of date. snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); if ( mysql_query( &dbconn, sql ) ) @@ -2188,197 +2076,9 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) } #if ZM_HAS_V4L -int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) -{ -<<<<<<< HEAD - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; -; - if ( device[0] ) { - sql += " AND Device='"; - sql += device; - sql += "'"; - } - if ( staticConfig.SERVER_ID ) { - sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); - } - Debug( 1, "Loading Local Monitors with %s", sql.c_str() ); +int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; - MYSQL_RES *result = zmDbFetch( sql.c_str() ); - if ( !result ) { - Error( "Can't load local monitors: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; - - const char *device = dbrow[col]; col++; - int channel = atoi(dbrow[col]); col++; - int format = atoi(dbrow[col]); col++; - bool v4l_multi_buffer = config.v4l_multi_buffer; - if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { - v4l_multi_buffer = false; - } else if ( *dbrow[col] == '1' ) { - v4l_multi_buffer = true; - } - } - col++; - - int v4l_captures_per_frame = 0; - if ( dbrow[col] ) { - v4l_captures_per_frame = atoi(dbrow[col]); - } else { - v4l_captures_per_frame = config.captures_per_frame; - } -Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); - col++; - const char *method = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - int palette = atoi(dbrow[col]); col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - int label_size = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; - unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - int signal_check_colour; - if ( dbrow[col][0] == '#' ) - signal_check_colour = strtol(dbrow[col]+1,0,16); - else - signal_check_colour = strtol(dbrow[col],0,16); - col++; - bool embed_exif = (*dbrow[col] != '0'); col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - int extras = (deinterlacing>>24)&0xff; - - Camera *camera = new LocalCamera( - id, - device, - channel, - format, - v4l_multi_buffer, - v4l_captures_per_frame, - method, - cam_width, - cam_height, - colours, - palette, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE, - extras - ); - - monitors[i] = new Monitor( - id, - name, - server_id, - storage_id, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - label_size, - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - analysis_fps, - analysis_update_delay, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - embed_exif, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - monitors[i]->AddPrivacyBitmask( zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); -} -#endif // ZM_HAS_V4L - -int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) -{ - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; - if ( staticConfig.SERVER_ID ) { - sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); -======= - std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; if ( device[0] ) { sql += " AND Device='"; sql += device; @@ -2398,13 +2098,13 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; @@ -2419,7 +2119,6 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c } else if ( *dbrow[col] == '1' ) { v4l_multi_buffer = true; } ->>>>>>> master } col++; @@ -2429,184 +2128,22 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c } else { v4l_captures_per_frame = config.captures_per_frame; } -Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); + Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); col++; const char *method = dbrow[col]; col++; -<<<<<<< HEAD - Debug( 1, "Loading Remote Monitors with %s", sql.c_str() ); - MYSQL_RES *result = zmDbFetch( sql.c_str() ); - if ( !result ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; - - std::string protocol = dbrow[col]; col++; - std::string method = dbrow[col]; col++; - std::string host = dbrow[col]; col++; - std::string port = dbrow[col]; col++; - std::string path = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - bool rtsp_describe = (*dbrow[col] != '0'); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - std::string event_prefix = dbrow[col]; col++; - std::string label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - int label_size = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; - unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - bool embed_exif = (*dbrow[col] != '0'); col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = 0; - if ( protocol == "http" ) - { - camera = new RemoteCameraHttp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#if HAVE_LIBAVFORMAT - else if ( protocol == "rtsp" ) - { - camera = new RemoteCameraRtsp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - rtsp_describe, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#endif // HAVE_LIBAVFORMAT - else - { - Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); - } - - monitors[i] = new Monitor( - id, - name.c_str(), - server_id, - storage_id, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - label_size, - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - analysis_fps, - analysis_update_delay, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - embed_exif, - purpose, - 0, - 0 - - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - monitors[i]->AddPrivacyBitmask( zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); -======= int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; int palette = atoi(dbrow[col]); col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; + + int savejpegs = atoi(dbrow[col]); col++; + int videowriter = atoi(dbrow[col]); col++; + std::string encoderparams = dbrow[col]; col++; + bool record_audio = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; @@ -2614,154 +2151,11 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); const char *event_prefix = dbrow[col]; col++; const char *label_format = dbrow[col]; col++; ->>>>>>> master int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int label_size = atoi(dbrow[col]); col++; -<<<<<<< HEAD -int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) -{ - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; - if ( file[0] ) { - sql += " AND Path='"; - sql += file; - sql += "'"; - } - if ( staticConfig.SERVER_ID ) { - sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); - } - Debug( 1, "Loading File Monitors with %s", sql.c_str() ); - MYSQL_RES *result = zmDbFetch( sql.c_str() ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; - - const char *path = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - int label_size = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; - unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - bool embed_exif = (*dbrow[col] != '0'); col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = new FileCamera( - id, - path, // File - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - - monitors[i] = new Monitor( - id, - name, - server_id, - storage_id, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - label_size, - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - analysis_fps, - analysis_update_delay, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - embed_exif, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - monitors[i]->AddPrivacyBitmask( zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); -======= int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; @@ -2779,7 +2173,6 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; ->>>>>>> master int signal_check_colour; if ( dbrow[col][0] == '#' ) @@ -2789,153 +2182,6 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu col++; bool embed_exif = (*dbrow[col] != '0'); col++; -<<<<<<< HEAD -#if HAVE_LIBAVFORMAT -int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) -{ - std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; - if ( file[0] ) { - sql += " AND Path = '"; - sql += file; - sql += "'"; - } - if ( staticConfig.SERVER_ID ) { - sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); - } - Debug( 1, "Loading FFMPEG Monitors with %s", sql.c_str() ); - MYSQL_RES *result = zmDbFetch( sql.c_str() ); - if ( ! result ) { - Error( "Cannot load FfmpegMonitors" ); - exit( mysql_errno( &dbconn ) ); - } - - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; - - const char *path = dbrow[col]; col++; - const char *method = dbrow[col]; col++; - const char *options = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - int label_size = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; - unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - bool embed_exif = (*dbrow[col] != '0'); col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = new FfmpegCamera( - id, - path, // File - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - - monitors[i] = new Monitor( - id, - name, - server_id, - storage_id, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - label_size, - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - analysis_fps, - analysis_update_delay, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - embed_exif, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - monitors[i]->AddPrivacyBitmask( zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); -======= int extras = (deinterlacing>>24)&0xff; Camera *camera = new LocalCamera( @@ -2955,20 +2201,25 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose hue, colour, purpose==CAPTURE, + record_audio, extras ); ->>>>>>> master monitors[i] = new Monitor( id, name, server_id, + storage_id, function, enabled, linked_monitors, camera, orientation, deinterlacing, + savejpegs, + videowriter, + encoderparams, + record_audio, event_prefix, label_format, Coord( label_x, label_y ), @@ -3016,24 +2267,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { -<<<<<<< HEAD - std::string sql = stringtf( "select Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id ); - - MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ); - if ( ! dbrow ) { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - Monitor *monitor = 0; - unsigned int col = 0; - - unsigned int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - std::string type = dbrow[col]; col++; -======= - std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; if ( staticConfig.SERVER_ID ) { sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); } @@ -3052,14 +2286,13 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; std::string name = dbrow[col]; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; ->>>>>>> master + unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; @@ -3077,6 +2310,11 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; bool rtsp_describe = (*dbrow[col] != '0'); col++; + int savejpegs = atoi(dbrow[col]); col++; + int videowriter = atoi(dbrow[col]); col++; + std::string encoderparams = dbrow[col]; col++; + bool record_audio = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; @@ -3109,8 +2347,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c bool embed_exif = (*dbrow[col] != '0'); col++; Camera *camera = 0; - if ( protocol == "http" ) - { + if ( protocol == "http" ) { camera = new RemoteCameraHttp( id, method, @@ -3124,12 +2361,12 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); } #if HAVE_LIBAVFORMAT - else if ( protocol == "rtsp" ) - { + else if ( protocol == "rtsp" ) { camera = new RemoteCameraRtsp( id, method, @@ -3144,53 +2381,12 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); } #endif // HAVE_LIBAVFORMAT - else - { -<<<<<<< HEAD - Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); - } - monitor = new Monitor( - id, - name.c_str(), - server_id, - storage_id, - function, - enabled, - linked_monitors.c_str(), - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - label_size, - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - analysis_fps, - analysis_update_delay, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - embed_exif, - purpose, - 0, - 0 -======= + else { Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); } @@ -3198,12 +2394,17 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c id, name.c_str(), server_id, + storage_id, function, enabled, linked_monitors, camera, orientation, deinterlacing, + savejpegs, + videowriter, + encoderparams, + record_audio, event_prefix.c_str(), label_format.c_str(), Coord( label_x, label_y ), @@ -3230,8 +2431,6 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c purpose, 0, 0 - ->>>>>>> master ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); @@ -3250,9 +2449,8 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c return( n_monitors ); } -int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) -{ - std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; +int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; if ( file[0] ) { sql += " AND Path='"; sql += file; @@ -3263,8 +2461,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu } Debug( 1, "Loading File Monitors with %s", sql.c_str() ); MYSQL_RES *result = zmDbFetch( sql.c_str() ); - if ( !result ) - { + if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } @@ -3272,13 +2469,13 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; @@ -3291,6 +2488,12 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu /* int palette = atoi(dbrow[col]); */ col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; + + int savejpegs = atoi(dbrow[col]); col++; + int videowriter = atoi(dbrow[col]); col++; + std::string encoderparams = dbrow[col]; col++; + bool record_audio = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; @@ -3332,19 +2535,25 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); monitors[i] = new Monitor( id, name, server_id, + storage_id, function, enabled, linked_monitors, camera, orientation, deinterlacing, + savejpegs, + videowriter, + encoderparams, + record_audio, event_prefix, label_format, Coord( label_x, label_y ), @@ -3392,7 +2601,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; + std::string sql = "select Id, Name, ServerId, StorageId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; if ( file[0] ) { sql += " AND Path = '"; sql += file; @@ -3412,13 +2621,13 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; @@ -3433,6 +2642,12 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose /* int palette = atoi(dbrow[col]); */ col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; + + int savejpegs = atoi(dbrow[col]); col++; + int videowriter = atoi(dbrow[col]); col++; + std::string encoderparams = dbrow[col]; col++; + bool record_audio = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; @@ -3476,19 +2691,25 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); monitors[i] = new Monitor( id, name, server_id, + storage_id, function, enabled, linked_monitors, camera, orientation, deinterlacing, + savejpegs, + videowriter, + encoderparams, + record_audio, event_prefix, label_format, Coord( label_x, label_y ), @@ -3534,41 +2755,11 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose } #endif // HAVE_LIBAVFORMAT -Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) -{ -<<<<<<< HEAD - bool alarm = false; - unsigned int score = 0; +Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { + std::string sql = stringtf( "select Id, Name, ServerId, StorageId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, SaveJPEGs, VideoWriter, EncoderParameters, RecordAudio, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id ); - if ( n_zones <= 0 ) return( alarm ); - - Storage *storage = this->getStorage(); - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", storage->Path(), id ); - } - ref_image.WriteJpeg( diag_path ); - } - - ref_image.Delta( comp_image, &delta_image); - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", storage->Path(), id ); - } - delta_image.WriteJpeg( diag_path ); -======= - std::string sql = stringtf( "select Id, Name, ServerId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id ); - - MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ); - if ( ! dbrow ) { + zmDbRow dbrow; + if ( ! dbrow.fetch( sql.c_str() ) ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } @@ -3578,6 +2769,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) unsigned int id = atoi(dbrow[col]); col++; std::string name = dbrow[col]; col++; unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + unsigned int storage_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; std::string type = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; @@ -3603,7 +2795,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) } else { v4l_captures_per_frame = config.captures_per_frame; } -Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); + Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); col++; std::string protocol = dbrow[col]; col++; @@ -3622,6 +2814,11 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; bool rtsp_describe = (*dbrow[col] != '0'); col++; + int savejpegs = atoi(dbrow[col]); col++; + int videowriter = atoi(dbrow[col]); col++; + std::string encoderparams = dbrow[col]; col++; + bool record_audio = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; @@ -3663,8 +2860,7 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); int extras = (deinterlacing>>24)&0xff; Camera *camera = 0; - if ( type == "Local" ) - { + if ( type == "Local" ) { #if ZM_HAS_V4L camera = new LocalCamera( id, @@ -3683,16 +2879,14 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); hue, colour, purpose==CAPTURE, + record_audio, extras ); #else // ZM_HAS_V4L Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); #endif // ZM_HAS_V4L - } - else if ( type == "Remote" ) - { - if ( protocol == "http" ) - { + } else if ( type == "Remote" ) { + if ( protocol == "http" ) { camera = new RemoteCameraHttp( id, method.c_str(), @@ -3706,11 +2900,10 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); - } - else if ( protocol == "rtsp" ) - { + } else if ( protocol == "rtsp" ) { #if HAVE_LIBAVFORMAT camera = new RemoteCameraRtsp( id, @@ -3726,20 +2919,16 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); #else // HAVE_LIBAVFORMAT Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); #endif // HAVE_LIBAVFORMAT ->>>>>>> master - } - else - { + } else { Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); } - } - else if ( type == "File" ) - { + } else if ( type == "File" ) { camera = new FileCamera( id, path.c_str(), @@ -3750,11 +2939,10 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); - } - else if ( type == "Ffmpeg" ) - { + } else if ( type == "Ffmpeg" ) { #if HAVE_LIBAVFORMAT camera = new FfmpegCamera( id, @@ -3768,14 +2956,13 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); #else // HAVE_LIBAVFORMAT Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); #endif // HAVE_LIBAVFORMAT - } - else if (type == "Libvlc") - { + } else if (type == "Libvlc") { #if HAVE_LIBVLC camera = new LibvlcCamera( id, @@ -3789,14 +2976,13 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); #else // HAVE_LIBVLC Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id ); #endif // HAVE_LIBVLC - } - else if ( type == "cURL" ) - { + } else if ( type == "cURL" ) { #if HAVE_LIBCURL camera = new cURLCamera( id, @@ -3810,26 +2996,30 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); contrast, hue, colour, - purpose==CAPTURE + purpose==CAPTURE, + record_audio ); #else // HAVE_LIBCURL Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id ); #endif // HAVE_LIBCURL - } - else - { + } else { Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); } monitor = new Monitor( id, name.c_str(), server_id, + storage_id, function, enabled, linked_monitors.c_str(), camera, orientation, deinterlacing, + savejpegs, + videowriter, + encoderparams, + record_audio, event_prefix.c_str(), label_format.c_str(), Coord( label_x, label_y ), @@ -3860,8 +3050,7 @@ Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); ); int n_zones = 0; - if ( load_zones ) - { + if ( load_zones ) { Zone **zones = 0; n_zones = Zone::Load( monitor, zones ); monitor->AddZones( n_zones, zones ); @@ -3884,22 +3073,38 @@ int Monitor::Capture() /* Copy the next image into the shared memory */ capture_image->CopyBuffer(*(next_buffer.image)); } - + /* Capture a new next image */ - captureResult = camera->Capture(*(next_buffer.image)); - - if ( FirstCapture ) { - FirstCapture = 0; - return 0; + + //Check if FFMPEG camera + if((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()){ + captureResult = camera->CaptureAndRecord(*(next_buffer.image), video_store_data->recording, video_store_data->event_file); + }else{ + captureResult = camera->Capture(*(next_buffer.image)); } + if ( FirstCapture ) { + FirstCapture = 0; + return 0; + } + } else { - /* Capture directly into image buffer, avoiding the need to memcpy() */ - captureResult = camera->Capture(*capture_image); + //Check if FFMPEG camera + if((GetOptVideoWriter() == 2) && camera->SupportsNativeVideo()){ + //Warning("ZMC: Recording: %d", video_store_data->recording); + captureResult = camera->CaptureAndRecord(*capture_image, video_store_data->recording, video_store_data->event_file); + }else{ + /* Capture directly into image buffer, avoiding the need to memcpy() */ + captureResult = camera->Capture(*capture_image); + } } - if ( captureResult != 0 ) - { + if((GetOptVideoWriter() == 2) && captureResult > 0){ + //video_store_data->frameNumber = captureResult; + captureResult = 0; + } + + if ( captureResult != 0 ) { // Unable to capture image for temporary reason // Fake a signal loss image Rgb signalcolor; @@ -3910,8 +3115,7 @@ int Monitor::Capture() captureResult = 1; } - if ( captureResult == 1 ) - { + if ( captureResult == 1 ) { /* Deinterlacing */ if ( (deinterlacing & 0xff) == 1 ) { @@ -3927,48 +3131,41 @@ int Monitor::Capture() } - if ( orientation != ROTATE_0 ) - { - switch ( orientation ) - { - case ROTATE_0 : - { + if ( orientation != ROTATE_0 ) { + switch ( orientation ) { + case ROTATE_0 : { // No action required break; } case ROTATE_90 : case ROTATE_180 : - case ROTATE_270 : - { + case ROTATE_270 : { capture_image->Rotate( (orientation-1)*90 ); break; } case FLIP_HORI : - case FLIP_VERT : - { + case FLIP_VERT : { capture_image->Flip( orientation==FLIP_HORI ); break; } } } + } // end if captureResults == 1 - } + // if true? let's get rid of this. if ( true ) { - if ( capture_image->Size() > camera->ImageSize() ) - { + if ( capture_image->Size() > camera->ImageSize() ) { Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() ); return( -1 ); } - if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) - { + if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) { Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); time_t now = time(0); double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); time_t last_read_delta = now - shared_data->last_read_time; - if ( last_read_delta > (image_buffer_count/approxFps) ) - { + if ( last_read_delta > (image_buffer_count/approxFps) ) { Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) shared_data->last_read_index = image_buffer_count; } @@ -3978,8 +3175,7 @@ int Monitor::Capture() capture_image->MaskPrivacy( privacy_bitmask ); gettimeofday( image_buffer[index].timestamp, NULL ); - if ( config.timestamp_on_capture ) - { + if ( config.timestamp_on_capture ) { TimestampImage( capture_image, image_buffer[index].timestamp ); } shared_data->signal = CheckSignal(capture_image); @@ -3988,8 +3184,7 @@ int Monitor::Capture() image_count++; - if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) - { + if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { time_t now = image_buffer[index].timestamp->tv_sec; fps = double(fps_report_interval)/(now-last_fps_time); //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); @@ -3998,16 +3193,14 @@ int Monitor::Capture() last_fps_time = now; } - if ( shared_data->action & GET_SETTINGS ) - { + if ( shared_data->action & GET_SETTINGS ) { shared_data->brightness = camera->Brightness(); shared_data->hue = camera->Hue(); shared_data->colour = camera->Colour(); shared_data->contrast = camera->Contrast(); shared_data->action &= ~GET_SETTINGS; } - if ( shared_data->action & SET_SETTINGS ) - { + if ( shared_data->action & SET_SETTINGS ) { camera->Brightness( shared_data->brightness ); camera->Hue( shared_data->hue ); camera->Colour( shared_data->colour ); @@ -4066,10 +3259,9 @@ void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) c bool Monitor::closeEvent() { - if ( event ) - { - if ( function == RECORD || function == MOCORD ) - { + video_store_data->recording = false; + if ( event ) { + if ( function == RECORD || function == MOCORD ) { gettimeofday( &(event->EndTime()), NULL ); } delete event; @@ -4086,12 +3278,14 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z if ( n_zones <= 0 ) return( alarm ); + Storage *storage = this->getStorage(); + if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", storage->Path(), id ); } ref_image.WriteJpeg( diag_path ); } @@ -4103,7 +3297,7 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", storage->Path(), id ); } delta_image.WriteJpeg( diag_path ); } @@ -4336,7 +3530,7 @@ bool Monitor::DumpSettings( char *output, bool verbose ) zones[i]->DumpSettings( output+strlen(output), verbose ); } return( true ); -} +} // bool Monitor::DumpSettings( char *output, bool verbose ) bool MonitorStream::checkSwapPath( const char *path, bool create_path ) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index bc51ff607..7ec371db2 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -48,9 +48,9 @@ // class Monitor { -friend class MonitorStream; + friend class MonitorStream; -public: + public: typedef enum { QUERY=0, @@ -87,7 +87,7 @@ public: TAPE } State; -protected: + protected: typedef std::set ZoneSet; typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action; @@ -97,43 +97,43 @@ protected: /* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */ typedef struct { - uint32_t size; /* +0 */ - uint32_t last_write_index; /* +4 */ - uint32_t last_read_index; /* +8 */ - uint32_t state; /* +12 */ - uint32_t last_event; /* +16 */ - uint32_t action; /* +20 */ - int32_t brightness; /* +24 */ - int32_t hue; /* +28 */ - int32_t colour; /* +32 */ - int32_t contrast; /* +36 */ - int32_t alarm_x; /* +40 */ - int32_t alarm_y; /* +44 */ - uint8_t valid; /* +48 */ - uint8_t active; /* +49 */ - uint8_t signal; /* +50 */ - uint8_t format; /* +51 */ - uint32_t imagesize; /* +52 */ - uint32_t epadding1; /* +56 */ - uint32_t epadding2; /* +60 */ + uint32_t size; /* +0 */ + uint32_t last_write_index; /* +4 */ + uint32_t last_read_index; /* +8 */ + uint32_t state; /* +12 */ + uint32_t last_event; /* +16 */ + uint32_t action; /* +20 */ + int32_t brightness; /* +24 */ + int32_t hue; /* +28 */ + int32_t colour; /* +32 */ + int32_t contrast; /* +36 */ + int32_t alarm_x; /* +40 */ + int32_t alarm_y; /* +44 */ + uint8_t valid; /* +48 */ + uint8_t active; /* +49 */ + uint8_t signal; /* +50 */ + uint8_t format; /* +51 */ + uint32_t imagesize; /* +52 */ + uint32_t epadding1; /* +56 */ + uint32_t epadding2; /* +60 */ /* - ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. - ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. - */ - union { /* +64 */ - time_t last_write_time; - uint64_t extrapad1; + ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. + ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. + */ + union { /* +64 */ + time_t last_write_time; + uint64_t extrapad1; }; - union { /* +72 */ - time_t last_read_time; - uint64_t extrapad2; + union { /* +72 */ + time_t last_read_time; + uint64_t extrapad2; }; - uint8_t control_state[256]; /* +80 */ - + uint8_t control_state[256]; /* +80 */ + } SharedData; typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; - + /* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */ typedef struct { @@ -149,406 +149,283 @@ protected: /* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */ struct Snapshot { - struct timeval *timestamp; - Image *image; + struct timeval *timestamp; + Image *image; void* padding; }; - class MonitorLink + //TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat +#if 1 + //sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit + typedef struct { - protected: - unsigned int id; - char name[64]; + uint32_t size; + char event_file[4096]; + uint32_t recording; //bool arch dependent so use uint32 instead + //uint32_t frameNumber; + } VideoStoreData; - bool connected; - time_t last_connect_time; +#endif // HAVE_LIBAVFORMAT + + class MonitorLink { + protected: + unsigned int id; + char name[64]; + + bool connected; + time_t last_connect_time; #if ZM_MEM_MAPPED - int map_fd; - char mem_file[PATH_MAX]; + int map_fd; + char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED - int shm_id; + int shm_id; #endif // ZM_MEM_MAPPED -<<<<<<< HEAD - off_t mem_size; - unsigned char *mem_ptr; + off_t mem_size; + unsigned char *mem_ptr; - volatile SharedData *shared_data; - volatile TriggerData *trigger_data; + volatile SharedData *shared_data; + volatile TriggerData *trigger_data; + volatile VideoStoreData *video_store_data; - int last_state; - int last_event; - + int last_state; + int last_event; - public: - MonitorLink( int p_id, const char *p_name ); - ~MonitorLink(); - inline int Id() const - { - return( id ); - } - inline const char *Name() const - { - return( name ); - } + public: + MonitorLink( int p_id, const char *p_name ); + ~MonitorLink(); - inline bool isConnected() const - { - return( connected ); - } - inline time_t getLastConnectTime() const - { - return( last_connect_time ); - } + inline int Id() const { + return( id ); + } + inline const char *Name() const { + return( name ); + } - bool connect(); - bool disconnect(); + inline bool isConnected() const { + return( connected ); + } + inline time_t getLastConnectTime() const { + return( last_connect_time ); + } - bool isAlarmed(); - bool inAlarm(); - bool hasAlarmed(); - }; + bool connect(); + bool disconnect(); -protected: - // These are read from the DB and thereafter remain unchanged - unsigned int id; - char name[64]; - unsigned int server_id; // Id of the Server object - unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more. - Function function; // What the monitor is doing - bool enabled; // Whether the monitor is enabled or asleep - unsigned int width; // Normally the same as the camera, but not if partly rotated - unsigned int height; // Normally the same as the camera, but not if partly rotated - bool v4l_multi_buffer; - unsigned int v4l_captures_per_frame; - Orientation orientation; // Whether the image has to be rotated at all - unsigned int deinterlacing; - int brightness; // The statically saved brightness of the camera - int contrast; // The statically saved contrast of the camera - int hue; // The statically saved hue of the camera - int colour; // The statically saved colour of the camera - char event_prefix[64]; // The prefix applied to event names as they are created - char label_format[64]; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images - int label_size; // Size of the timestamp on the images - int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count - int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, - // value is pre_event_count + alarm_frame_count - 1 - int warmup_count; // How many images to process before looking for events - int pre_event_count; // How many images to hold and prepend to an alarm event - int post_event_count; // How many unalarmed images must occur before the alarm state is reset - int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now - int section_length; // How long events should last in continuous modes - bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor - int frame_skip; // How many frames to skip in continuous modes - int motion_frame_skip; // How many frames to skip in motion detection - double analysis_fps; // Target framerate for video analysis - unsigned int analysis_update_delay; // How long we wait before updating analysis parameters - int capture_delay; // How long we wait between capture frames - int alarm_capture_delay; // How long we wait between capture frames when in alarm state - int alarm_frame_count; // How many alarm frames are required before an event is triggered - int fps_report_interval; // How many images should be captured/processed between reporting the current FPS - int ref_blend_perc; // Percentage of new image going into reference image. - int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. - bool track_motion; // Whether this monitor tries to track detected motion - Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected - bool embed_exif; // Whether to embed Exif data into each image frame or not - - double fps; - Image delta_image; - Image ref_image; - Image alarm_image; // Used in creating analysis images, will be initialized in Analysis - Image write_image; // Used when creating snapshot images - - Purpose purpose; // What this monitor has been created to do - int event_count; - int image_count; - int ready_count; - int first_alarm_count; - int last_alarm_count; - int buffer_count; - int prealarm_count; - State state; - time_t start_time; - time_t last_fps_time; - time_t auto_resume_time; - unsigned int last_motion_score; - - EventCloseMode event_close_mode; -======= - off_t mem_size; - unsigned char *mem_ptr; - - volatile SharedData *shared_data; - volatile TriggerData *trigger_data; - - int last_state; - int last_event; - - public: - MonitorLink( int p_id, const char *p_name ); - ~MonitorLink(); - - inline int Id() const - { - return( id ); - } - inline const char *Name() const - { - return( name ); - } - - inline bool isConnected() const - { - return( connected ); - } - inline time_t getLastConnectTime() const - { - return( last_connect_time ); - } - - bool connect(); - bool disconnect(); - - bool isAlarmed(); - bool inAlarm(); - bool hasAlarmed(); + bool isAlarmed(); + bool inAlarm(); + bool hasAlarmed(); }; -protected: + protected: // These are read from the DB and thereafter remain unchanged - unsigned int id; - char name[64]; - unsigned int server_id; - Function function; // What the monitor is doing - bool enabled; // Whether the monitor is enabled or asleep - unsigned int width; // Normally the same as the camera, but not if partly rotated - unsigned int height; // Normally the same as the camera, but not if partly rotated - bool v4l_multi_buffer; - unsigned int v4l_captures_per_frame; - Orientation orientation; // Whether the image has to be rotated at all - unsigned int deinterlacing; - int brightness; // The statically saved brightness of the camera - int contrast; // The statically saved contrast of the camera - int hue; // The statically saved hue of the camera - int colour; // The statically saved colour of the camera - char event_prefix[64]; // The prefix applied to event names as they are created - char label_format[64]; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images - int label_size; // Size of the timestamp on the images - int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count - int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, - // value is pre_event_count + alarm_frame_count - 1 - int warmup_count; // How many images to process before looking for events - int pre_event_count; // How many images to hold and prepend to an alarm event - int post_event_count; // How many unalarmed images must occur before the alarm state is reset - int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now - int section_length; // How long events should last in continuous modes - bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor - int frame_skip; // How many frames to skip in continuous modes - int motion_frame_skip; // How many frames to skip in motion detection - double analysis_fps; // Target framerate for video analysis - unsigned int analysis_update_delay; // How long we wait before updating analysis parameters - int capture_delay; // How long we wait between capture frames - int alarm_capture_delay; // How long we wait between capture frames when in alarm state - int alarm_frame_count; // How many alarm frames are required before an event is triggered - int fps_report_interval; // How many images should be captured/processed between reporting the current FPS - int ref_blend_perc; // Percentage of new image going into reference image. - int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. - bool track_motion; // Whether this monitor tries to track detected motion - Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected - bool embed_exif; // Whether to embed Exif data into each image frame or not + unsigned int id; + char name[64]; + unsigned int server_id; // Id of the Server object + unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more. + Function function; // What the monitor is doing + bool enabled; // Whether the monitor is enabled or asleep + unsigned int width; // Normally the same as the camera, but not if partly rotated + unsigned int height; // Normally the same as the camera, but not if partly rotated + bool v4l_multi_buffer; + unsigned int v4l_captures_per_frame; + Orientation orientation; // Whether the image has to be rotated at all + unsigned int deinterlacing; - double fps; - Image delta_image; - Image ref_image; - Image alarm_image; // Used in creating analysis images, will be initialized in Analysis - Image write_image; // Used when creating snapshot images + int savejpegspref; + int videowriterpref; + std::string encoderparams; + std::vector encoderparamsvec; + bool record_audio; // Whether to store the audio that we receive - Purpose purpose; // What this monitor has been created to do - int event_count; - int image_count; - int ready_count; - int first_alarm_count; - int last_alarm_count; - int buffer_count; - int prealarm_count; - State state; - time_t start_time; - time_t last_fps_time; - time_t auto_resume_time; - unsigned int last_motion_score; + int brightness; // The statically saved brightness of the camera + int contrast; // The statically saved contrast of the camera + int hue; // The statically saved hue of the camera + int colour; // The statically saved colour of the camera + char event_prefix[64]; // The prefix applied to event names as they are created + char label_format[64]; // The format of the timestamp on the images + Coord label_coord; // The coordinates of the timestamp on the images + int label_size; // Size of the timestamp on the images + int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count + int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, + // value is pre_event_count + alarm_frame_count - 1 + int warmup_count; // How many images to process before looking for events + int pre_event_count; // How many images to hold and prepend to an alarm event + int post_event_count; // How many unalarmed images must occur before the alarm state is reset + int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now + int section_length; // How long events should last in continuous modes + bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor + int frame_skip; // How many frames to skip in continuous modes + int motion_frame_skip; // How many frames to skip in motion detection + double analysis_fps; // Target framerate for video analysis + unsigned int analysis_update_delay; // How long we wait before updating analysis parameters + int capture_delay; // How long we wait between capture frames + int alarm_capture_delay; // How long we wait between capture frames when in alarm state + int alarm_frame_count; // How many alarm frames are required before an event is triggered + int fps_report_interval; // How many images should be captured/processed between reporting the current FPS + int ref_blend_perc; // Percentage of new image going into reference image. + int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. + bool track_motion; // Whether this monitor tries to track detected motion + Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected + bool embed_exif; // Whether to embed Exif data into each image frame or not + + double fps; + Image delta_image; + Image ref_image; + Image alarm_image; // Used in creating analysis images, will be initialized in Analysis + Image write_image; // Used when creating snapshot images + + Purpose purpose; // What this monitor has been created to do + int event_count; + int image_count; + int ready_count; + int first_alarm_count; + int last_alarm_count; + int buffer_count; + int prealarm_count; + State state; + time_t start_time; + time_t last_fps_time; + time_t auto_resume_time; + unsigned int last_motion_score; EventCloseMode event_close_mode; ->>>>>>> master #if ZM_MEM_MAPPED - int map_fd; - char mem_file[PATH_MAX]; + int map_fd; + char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED - int shm_id; + int shm_id; #endif // ZM_MEM_MAPPED -<<<<<<< HEAD off_t mem_size; unsigned char *mem_ptr; Storage *storage; SharedData *shared_data; TriggerData *trigger_data; -======= - off_t mem_size; - unsigned char *mem_ptr; ->>>>>>> master + VideoStoreData *video_store_data; - SharedData *shared_data; - TriggerData *trigger_data; + Snapshot *image_buffer; + Snapshot next_buffer; /* Used by four field deinterlacing */ + Snapshot *pre_event_buffer; - Snapshot *image_buffer; - Snapshot next_buffer; /* Used by four field deinterlacing */ - Snapshot *pre_event_buffer; + Camera *camera; - Camera *camera; + Event *event; - Event *event; + int n_zones; + Zone **zones; - int n_zones; - Zone **zones; + struct timeval **timestamps; + Image **images; - struct timeval **timestamps; - Image **images; + const unsigned char *privacy_bitmask; - const unsigned char *privacy_bitmask; + int n_linked_monitors; + MonitorLink **linked_monitors; - int n_linked_monitors; - MonitorLink **linked_monitors; - -public: -// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. -//bool OurCheckAlarms( Zone *zone, const Image *pImage ); -<<<<<<< HEAD - Monitor( int p_id, const char *p_name, unsigned int p_server_id, unsigned int p_storage_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); - ~Monitor(); - - void AddZones( int p_n_zones, Zone *p_zones[] ); - void AddPrivacyBitmask( Zone *p_zones[] ); - - bool connect(); - inline int ShmValid() const - { - return( shared_data->valid ); - } - - inline int Id() const - { - return( id ); - } - inline const char *Name() const - { - return( name ); - } - inline Storage *getStorage() - { - if ( ! storage ) { - storage = new Storage( storage_id ); - } - return( storage ); - } - inline Function GetFunction() const - { - return( function ); - } - inline bool Enabled() - { - if ( function <= MONITOR ) - return( false ); - return( enabled ); - } - inline const char *EventPrefix() const - { - return( event_prefix ); - } - inline bool Ready() - { - if ( function <= MONITOR ) - return( false ); - return( image_count > ready_count ); - } - inline bool Active() - { - if ( function <= MONITOR ) - return( false ); - return( enabled && shared_data->active ); - } - inline bool Exif() - { - return( embed_exif ); - } - - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } - unsigned int Colours() const { return( camera->Colours() ); } - unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } - -======= - Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); + public: + // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. + //bool OurCheckAlarms( Zone *zone, const Image *pImage ); + Monitor( + int p_id, + const char *p_name, + unsigned int p_server_id, + unsigned int p_storage_id, + int p_function, + bool p_enabled, + const char *p_linked_monitors, + Camera *p_camera, + int p_orientation, + unsigned int p_deinterlacing, + int p_savejpegs, + int p_videowriter, + std::string p_encoderparams, + bool p_record_audio, + const char *p_event_prefix, + const char *p_label_format, + const Coord &p_label_coord, + int label_size, + int p_image_buffer_count, + int p_warmup_count, + int p_pre_event_count, + int p_post_event_count, + int p_stream_replay_buffer, + int p_alarm_frame_count, + int p_section_length, + int p_frame_skip, + int p_motion_frame_skip, + double p_analysis_fps, + unsigned int p_analysis_update_delay, + int p_capture_delay, + int p_alarm_capture_delay, + int p_fps_report_interval, + int p_ref_blend_perc, + int p_alarm_ref_blend_perc, + bool p_track_motion, + Rgb p_signal_check_colour, + bool p_embed_exif, + Purpose p_purpose, + int p_n_zones=0, + Zone *p_zones[]=0 + ); ~Monitor(); void AddZones( int p_n_zones, Zone *p_zones[] ); void AddPrivacyBitmask( Zone *p_zones[] ); bool connect(); - inline int ShmValid() const - { + inline int ShmValid() const { return( shared_data->valid ); } - inline int Id() const - { + inline int Id() const { return( id ); } - inline const char *Name() const - { + inline const char *Name() const { return( name ); } - inline Function GetFunction() const - { + inline Storage *getStorage() { + if ( ! storage ) { + storage = new Storage( storage_id ); + } + return( storage ); + } + inline Function GetFunction() const { return( function ); } - inline bool Enabled() - { + inline bool Enabled() { if ( function <= MONITOR ) return( false ); return( enabled ); } - inline const char *EventPrefix() const - { + inline const char *EventPrefix() const { return( event_prefix ); } - inline bool Ready() - { + inline bool Ready() { if ( function <= MONITOR ) return( false ); return( image_count > ready_count ); } - inline bool Active() - { + inline bool Active() { if ( function <= MONITOR ) return( false ); return( enabled && shared_data->active ); } - inline bool Exif() - { + inline bool Exif() { return( embed_exif ); } - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } + unsigned int Width() const { return width; } + unsigned int Height() const { return height; } unsigned int Colours() const { return( camera->Colours() ); } unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } - ->>>>>>> master - + + int GetOptSaveJPEGs() const { return( savejpegspref ); } + int GetOptVideoWriter() const { return( videowriterpref ); } + const std::vector* GetOptEncoderParams() const { return( &encoderparamsvec ); } + State GetState() const; int GetImage( int index=-1, int scale=100 ); struct timeval GetTimestamp( int index=-1 ) const; @@ -577,23 +454,20 @@ public: int actionColour( int p_colour=-1 ); int actionContrast( int p_contrast=-1 ); - inline int PrimeCapture() - { + inline int PrimeCapture() { return( camera->PrimeCapture() ); } - inline int PreCapture() - { + inline int PreCapture() { return( camera->PreCapture() ); } int Capture(); - int PostCapture() - { + int PostCapture() { return( camera->PostCapture() ); } unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); - // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. - //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); + // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. + //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); bool CheckSignal( const Image *image ); bool Analyse(); void DumpImage( Image *dump_image ) const; @@ -630,54 +504,49 @@ public: #define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit)) -class MonitorStream : public StreamBase -{ -protected: - typedef struct SwapImage { - bool valid; - struct timeval timestamp; - char file_name[PATH_MAX]; - } SwapImage; +class MonitorStream : public StreamBase { + protected: + typedef struct SwapImage { + bool valid; + struct timeval timestamp; + char file_name[PATH_MAX]; + } SwapImage; -private: - SwapImage *temp_image_buffer; - int temp_image_buffer_count; - int temp_read_index; - int temp_write_index; + private: + SwapImage *temp_image_buffer; + int temp_image_buffer_count; + int temp_read_index; + int temp_write_index; -protected: - time_t ttl; + protected: + time_t ttl; -protected: - int playback_buffer; - bool delayed; + protected: + int playback_buffer; + bool delayed; - int frame_count; + int frame_count; -protected: - bool checkSwapPath( const char *path, bool create_path ); + protected: + bool checkSwapPath( const char *path, bool create_path ); - bool sendFrame( const char *filepath, struct timeval *timestamp ); - bool sendFrame( Image *image, struct timeval *timestamp ); - void processCommand( const CmdMsg *msg ); + bool sendFrame( const char *filepath, struct timeval *timestamp ); + bool sendFrame( Image *image, struct timeval *timestamp ); + void processCommand( const CmdMsg *msg ); -public: - MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) - { - } - void setStreamBuffer( int p_playback_buffer ) - { - playback_buffer = p_playback_buffer; - } - void setStreamTTL( time_t p_ttl ) - { - ttl = p_ttl; - } - bool setStreamStart( int monitor_id ) - { - return loadMonitor( monitor_id ); - } - void runStream(); + public: + MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) { + } + void setStreamBuffer( int p_playback_buffer ) { + playback_buffer = p_playback_buffer; + } + void setStreamTTL( time_t p_ttl ) { + ttl = p_ttl; + } + bool setStreamStart( int monitor_id ) { + return loadMonitor( monitor_id ); + } + void runStream(); }; #endif // ZM_MONITOR_H diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index 69ca89379..1cdf73057 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -34,242 +34,242 @@ extern "C" bool VideoStream::initialised = false; VideoStream::MimeData VideoStream::mime_data[] = { - { "asf", "video/x-ms-asf" }, - { "swf", "application/x-shockwave-flash" }, - { "flv", "video/x-flv" }, - { "mov", "video/quicktime" } + { "asf", "video/x-ms-asf" }, + { "swf", "application/x-shockwave-flash" }, + { "flv", "video/x-flv" }, + { "mov", "video/quicktime" } }; void VideoStream::Initialise( ) { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); - av_register_all( ); + av_register_all( ); #if LIBAVFORMAT_VERSION_CHECK(53, 13, 0, 19, 0) - avformat_network_init(); + avformat_network_init(); #endif - initialised = true; + initialised = true; } void VideoStream::SetupFormat( ) { - /* allocate the output media context */ - ofc = NULL; + /* allocate the output media context */ + ofc = NULL; #if (LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 2, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - avformat_alloc_output_context2( &ofc, NULL, format, filename ); + avformat_alloc_output_context2( &ofc, NULL, format, filename ); #else - AVFormatContext *s= avformat_alloc_context(); - if(!s) - { - Fatal( "avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc) ); - } + AVFormatContext *s= avformat_alloc_context(); + if(!s) + { + Fatal( "avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc) ); + } - AVOutputFormat *oformat; - if (format) { + AVOutputFormat *oformat; + if (format) { #if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0) - oformat = av_guess_format(format, NULL, NULL); + oformat = av_guess_format(format, NULL, NULL); #else - oformat = guess_format(format, NULL, NULL); + oformat = guess_format(format, NULL, NULL); #endif - if (!oformat) { - Fatal( "Requested output format '%s' is not a suitable output format", format ); - } - } else { + if (!oformat) { + Fatal( "Requested output format '%s' is not a suitable output format", format ); + } + } else { #if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0) - oformat = av_guess_format(NULL, filename, NULL); + oformat = av_guess_format(NULL, filename, NULL); #else - oformat = guess_format(NULL, filename, NULL); + oformat = guess_format(NULL, filename, NULL); #endif - if (!oformat) { - Fatal( "Unable to find a suitable output format for '%s'", format ); - } - } - s->oformat = oformat; - - if (s->oformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->oformat->priv_data_size); - if (!s->priv_data) - { - Fatal( "Could not allocate private data for output format." ); - } + if (!oformat) { + Fatal( "Unable to find a suitable output format for '%s'", format ); + } + } + s->oformat = oformat; + + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (!s->priv_data) + { + Fatal( "Could not allocate private data for output format." ); + } #if LIBAVFORMAT_VERSION_CHECK(52, 92, 0, 92, 0) - if (s->oformat->priv_class) { - *(const AVClass**)s->priv_data = s->oformat->priv_class; - av_opt_set_defaults(s->priv_data); - } + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data = s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } #endif - } - else - { - s->priv_data = NULL; - } - - if(filename) - { - snprintf( s->filename, sizeof(s->filename), "%s", filename ); - } - - ofc = s; + } + else + { + s->priv_data = NULL; + } + + if(filename) + { + snprintf( s->filename, sizeof(s->filename), "%s", filename ); + } + + ofc = s; #endif - if ( !ofc ) - { - Fatal( "avformat_alloc_..._context failed: %d", ofc ); - } + if ( !ofc ) + { + Fatal( "avformat_alloc_..._context failed: %d", ofc ); + } - of = ofc->oformat; - Debug( 1, "Using output format: %s (%s)", of->name, of->long_name ); + of = ofc->oformat; + Debug( 1, "Using output format: %s (%s)", of->name, of->long_name ); } void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) { - /* ffmpeg format matching */ - switch(colours) { - case ZM_COLOUR_RGB24: - { - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - pf = AV_PIX_FMT_BGR24; - } else { - /* Assume RGB subpixel order */ - pf = AV_PIX_FMT_RGB24; - } - break; - } - case ZM_COLOUR_RGB32: - { - if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - pf = AV_PIX_FMT_ARGB; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - pf = AV_PIX_FMT_ABGR; - } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - pf = AV_PIX_FMT_BGRA; - } else { - /* Assume RGBA subpixel order */ - pf = AV_PIX_FMT_RGBA; - } - break; - } - case ZM_COLOUR_GRAY8: - pf = AV_PIX_FMT_GRAY8; - break; - default: - Panic("Unexpected colours: %d",colours); - break; - } + /* ffmpeg format matching */ + switch(colours) { + case ZM_COLOUR_RGB24: + { + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + pf = AV_PIX_FMT_BGR24; + } else { + /* Assume RGB subpixel order */ + pf = AV_PIX_FMT_RGB24; + } + break; + } + case ZM_COLOUR_RGB32: + { + if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + pf = AV_PIX_FMT_ARGB; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + pf = AV_PIX_FMT_ABGR; + } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + pf = AV_PIX_FMT_BGRA; + } else { + /* Assume RGBA subpixel order */ + pf = AV_PIX_FMT_RGBA; + } + break; + } + case ZM_COLOUR_GRAY8: + pf = AV_PIX_FMT_GRAY8; + break; + default: + Panic("Unexpected colours: %d",colours); + break; + } - if ( strcmp( "rtp", of->name ) == 0 ) - { - // RTP must have a packet_size. - // Not sure what this value should be really... - ofc->packet_size = width*height; - - if ( of->video_codec == AV_CODEC_ID_NONE) - { - // RTP does not have a default codec in ffmpeg <= 0.8. - of->video_codec = AV_CODEC_ID_MPEG4; - } - } - - _AVCODECID codec_id = of->video_codec; - if ( codec_name ) - { - AVCodec *a = avcodec_find_encoder_by_name(codec_name); - if ( a ) - { - codec_id = a->id; - } - else - { + if ( strcmp( "rtp", of->name ) == 0 ) + { + // RTP must have a packet_size. + // Not sure what this value should be really... + ofc->packet_size = width*height; + + if ( of->video_codec == AV_CODEC_ID_NONE) + { + // RTP does not have a default codec in ffmpeg <= 0.8. + of->video_codec = AV_CODEC_ID_MPEG4; + } + } + + _AVCODECID codec_id = of->video_codec; + if ( codec_name ) + { + AVCodec *a = avcodec_find_encoder_by_name(codec_name); + if ( a ) + { + codec_id = a->id; + } + else + { #if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); + Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); #else - Debug( 1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id ); + Debug( 1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id ); #endif - } - } + } + } - /* add the video streams using the default format codecs - and initialize the codecs */ - ost = NULL; - if ( codec_id != AV_CODEC_ID_NONE ) - { - codec = avcodec_find_encoder( codec_id ); - if ( !codec ) - { + /* add the video streams using the default format codecs + and initialize the codecs */ + ost = NULL; + if ( codec_id != AV_CODEC_ID_NONE ) + { + codec = avcodec_find_encoder( codec_id ); + if ( !codec ) + { #if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Fatal( "Could not find encoder for '%s'", avcodec_get_name( codec_id ) ); + Fatal( "Could not find encoder for '%s'", avcodec_get_name( codec_id ) ); #else - Fatal( "Could not find encoder for '%d'", codec_id ); + Fatal( "Could not find encoder for '%d'", codec_id ); #endif - } + } #if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Debug( 1, "Found encoder for '%s'", avcodec_get_name( codec_id ) ); + Debug( 1, "Found encoder for '%s'", avcodec_get_name( codec_id ) ); #else - Debug( 1, "Found encoder for '%d'", codec_id ); + Debug( 1, "Found encoder for '%d'", codec_id ); #endif #if LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) - ost = avformat_new_stream( ofc, codec ); + ost = avformat_new_stream( ofc, codec ); #else - ost = av_new_stream( ofc, 0 ); + ost = av_new_stream( ofc, 0 ); #endif - - if ( !ost ) - { - Fatal( "Could not alloc stream" ); - } - ost->id = ofc->nb_streams - 1; + + if ( !ost ) + { + Fatal( "Could not alloc stream" ); + } + ost->id = ofc->nb_streams - 1; - Debug( 1, "Allocated stream" ); + Debug( 1, "Allocated stream" ); - AVCodecContext *c = ost->codec; + AVCodecContext *c = ost->codec; - c->codec_id = codec->id; - c->codec_type = codec->type; + c->codec_id = codec->id; + c->codec_type = codec->type; - c->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; - if ( bitrate <= 100 ) - { - // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. - // This gets rid of artifacts in the beginning of the movie; and well, even quality. - c->flags |= CODEC_FLAG_QSCALE; - c->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0))); - } - else - { - c->bit_rate = bitrate; - } + c->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; + if ( bitrate <= 100 ) + { + // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. + // This gets rid of artifacts in the beginning of the movie; and well, even quality. + c->flags |= CODEC_FLAG_QSCALE; + c->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0))); + } + else + { + c->bit_rate = bitrate; + } - /* resolution must be a multiple of two */ - c->width = width; - c->height = height; - /* time base: this is the fundamental unit of time (in seconds) in terms - of which frame timestamps are represented. for fixed-fps content, - timebase should be 1/framerate and timestamp increments should be - identically 1. */ - c->time_base.den = frame_rate; - c->time_base.num = 1; - - Debug( 1, "Will encode in %d fps.", c->time_base.den ); - - /* emit one intra frame every second */ - c->gop_size = frame_rate; + /* resolution must be a multiple of two */ + c->width = width; + c->height = height; + /* time base: this is the fundamental unit of time (in seconds) in terms + of which frame timestamps are represented. for fixed-fps content, + timebase should be 1/framerate and timestamp increments should be + identically 1. */ + c->time_base.den = frame_rate; + c->time_base.num = 1; + + Debug( 1, "Will encode in %d fps.", c->time_base.den ); + + /* emit one intra frame every second */ + c->gop_size = frame_rate; - // some formats want stream headers to be separate - if ( of->flags & AVFMT_GLOBALHEADER ) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - } - else - { - Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); - } + // some formats want stream headers to be separate + if ( of->flags & AVFMT_GLOBALHEADER ) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + else + { + Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); + } } void VideoStream::SetParameters( ) @@ -278,512 +278,536 @@ void VideoStream::SetParameters( ) const char *VideoStream::MimeType( ) const { - for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) - { - if ( strcmp( format, mime_data[i].format ) == 0 ) - { - Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); - return ( mime_data[i].mime_type); - } - } - const char *mime_type = of->mime_type; - if ( !mime_type ) - { - std::string mime = "video/"; - mime = mime.append( format ); - mime_type = mime.c_str( ); - Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); - } + for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) + { + if ( strcmp( format, mime_data[i].format ) == 0 ) + { + Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); + return ( mime_data[i].mime_type); + } + } + const char *mime_type = of->mime_type; + if ( !mime_type ) + { + std::string mime = "video/"; + mime = mime.append( format ); + mime_type = mime.c_str( ); + Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); + } - Debug( 1, "MimeType is \"%s\"", mime_type ); + Debug( 1, "MimeType is \"%s\"", mime_type ); - return ( mime_type); + return ( mime_type); } void VideoStream::OpenStream( ) { - int avRet; + int avRet; - /* now that all the parameters are set, we can open the - video codecs and allocate the necessary encode buffers */ - if ( ost ) - { - AVCodecContext *c = ost->codec; - - /* open the codec */ + /* now that all the parameters are set, we can open the + video codecs and allocate the necessary encode buffers */ + if ( ost ) + { + AVCodecContext *c = ost->codec; + + /* open the codec */ #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - if ( (avRet = avcodec_open( c, codec )) < 0 ) + if ( (avRet = avcodec_open( c, codec )) < 0 ) #else - if ( (avRet = avcodec_open2( c, codec, 0 )) < 0 ) + if ( (avRet = avcodec_open2( c, codec, 0 )) < 0 ) #endif - { - Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) ); - } + { + Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) ); + } - Debug( 1, "Opened codec" ); + Debug( 1, "Opened codec" ); - /* allocate the encoded raw picture */ + /* allocate the encoded raw picture */ #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - opicture = av_frame_alloc( ); + opicture = av_frame_alloc( ); #else - opicture = avcodec_alloc_frame( ); + opicture = avcodec_alloc_frame( ); #endif - if ( !opicture ) - { - Panic( "Could not allocate opicture" ); - } - - int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); - uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); - if ( !opicture_buf ) - { -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &opicture ); + if ( !opicture ) + { + Panic( "Could not allocate opicture" ); + } + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int size = av_image_get_buffer_size( c->pix_fmt, c->width, + c->height, 1 ); #else - av_freep( &opicture ); + int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); #endif - Panic( "Could not allocate opicture_buf" ); - } - avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height ); - /* if the output format is not identical to the input format, then a temporary - picture is needed too. It is then converted to the required - output format */ - tmp_opicture = NULL; - if ( c->pix_fmt != pf ) - { + uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); + if ( !opicture_buf ) + { #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - tmp_opicture = av_frame_alloc( ); + av_frame_free( &opicture ); #else - tmp_opicture = avcodec_alloc_frame( ); + av_freep( &opicture ); #endif - if ( !tmp_opicture ) - { - Panic( "Could not allocate tmp_opicture" ); - } - int size = avpicture_get_size( pf, c->width, c->height ); - uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); - if ( !tmp_opicture_buf ) - { -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &tmp_opicture ); + Panic( "Could not allocate opicture_buf" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(opicture->data, opicture->linesize, + opicture_buf, c->pix_fmt, c->width, c->height, 1); #else - av_freep( &tmp_opicture ); + avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, + c->width, c->height ); #endif - Panic( "Could not allocate tmp_opicture_buf" ); - } - avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height ); - } - } - /* open the output file, if needed */ - if ( !(of->flags & AVFMT_NOFILE) ) - { - int ret; + /* if the output format is not identical to the input format, then a temporary + picture is needed too. It is then converted to the required + output format */ + tmp_opicture = NULL; + if ( c->pix_fmt != pf ) + { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + tmp_opicture = av_frame_alloc( ); +#else + tmp_opicture = avcodec_alloc_frame( ); +#endif + if ( !tmp_opicture ) + { + Panic( "Could not allocate tmp_opicture" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int size = av_image_get_buffer_size( pf, c->width, + c->height,1 ); +#else + int size = avpicture_get_size( pf, c->width, c->height ); +#endif + uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); + if ( !tmp_opicture_buf ) + { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &tmp_opicture ); +#else + av_freep( &tmp_opicture ); +#endif + Panic( "Could not allocate tmp_opicture_buf" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(tmp_opicture->data, + tmp_opicture->linesize, tmp_opicture_buf, pf, + c->width, c->height, 1); +#else + avpicture_fill( (AVPicture *)tmp_opicture, + tmp_opicture_buf, pf, c->width, c->height ); +#endif + } + } + + /* open the output file, if needed */ + if ( !(of->flags & AVFMT_NOFILE) ) + { + int ret; #if LIBAVFORMAT_VERSION_CHECK(53, 15, 0, 21, 0) - ret = avio_open2( &ofc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL ); + ret = avio_open2( &ofc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL ); #elif LIBAVFORMAT_VERSION_CHECK(52, 102, 0, 102, 0) - ret = avio_open( &ofc->pb, filename, AVIO_FLAG_WRITE ); + ret = avio_open( &ofc->pb, filename, AVIO_FLAG_WRITE ); #else - ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE ); + ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE ); #endif - if ( ret < 0 ) - { - Fatal( "Could not open '%s'", filename ); - } + if ( ret < 0 ) + { + Fatal( "Could not open '%s'", filename ); + } - Debug( 1, "Opened output \"%s\"", filename ); - } - else - { - Fatal( "of->flags & AVFMT_NOFILE" ); - } + Debug( 1, "Opened output \"%s\"", filename ); + } + else + { + Fatal( "of->flags & AVFMT_NOFILE" ); + } - video_outbuf = NULL; - if ( !(of->flags & AVFMT_RAWPICTURE) ) - { - /* allocate output buffer */ - /* XXX: API change will be done */ - // TODO: Make buffer dynamic. - video_outbuf_size = 4000000; - video_outbuf = (uint8_t *)malloc( video_outbuf_size ); - if ( video_outbuf == NULL ) { - Fatal("Unable to malloc memory for outbuf"); - } - } + video_outbuf = NULL; + if ( !(of->flags & AVFMT_RAWPICTURE) ) + { + /* allocate output buffer */ + /* XXX: API change will be done */ + // TODO: Make buffer dynamic. + video_outbuf_size = 4000000; + video_outbuf = (uint8_t *)malloc( video_outbuf_size ); + if ( video_outbuf == NULL ) { + Fatal("Unable to malloc memory for outbuf"); + } + } #if LIBAVFORMAT_VERSION_CHECK(52, 101, 0, 101, 0) - av_dump_format(ofc, 0, filename, 1); + av_dump_format(ofc, 0, filename, 1); #else - dump_format(ofc, 0, filename, 1); + dump_format(ofc, 0, filename, 1); #endif #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) - int ret = av_write_header( ofc ); + int ret = av_write_header( ofc ); #else - int ret = avformat_write_header( ofc, NULL ); + int ret = avformat_write_header( ofc, NULL ); #endif - if ( ret < 0 ) - { - Fatal( "?_write_header failed with error %d \"%s\"", ret, av_err2str( ret ) ); - } + if ( ret < 0 ) + { + Fatal( "?_write_header failed with error %d \"%s\"", ret, av_err2str( ret ) ); + } } VideoStream::VideoStream( const char *in_filename, const char *in_format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) : - filename(in_filename), - format(in_format), - last_pts( -1 ), - streaming_thread(0), - do_streaming(true), - buffer_copy(NULL), - buffer_copy_lock(new pthread_mutex_t), - buffer_copy_size(0), - buffer_copy_used(0), - packet_index(0) + filename(in_filename), + format(in_format), + last_pts( -1 ), + streaming_thread(0), + do_streaming(true), + buffer_copy(NULL), + buffer_copy_lock(new pthread_mutex_t), + buffer_copy_size(0), + buffer_copy_used(0), + packet_index(0) { - if ( !initialised ) - { - Initialise( ); - } - - if ( format ) - { - int length = strlen(format); - codec_and_format = new char[length+1];; - strcpy( codec_and_format, format ); - format = codec_and_format; - codec_name = NULL; - char *f = strchr(codec_and_format, '/'); - if (f != NULL) - { - *f = 0; - codec_name = f+1; - } - } + if ( !initialised ) + { + Initialise( ); + } + + if ( format ) + { + int length = strlen(format); + codec_and_format = new char[length+1];; + strcpy( codec_and_format, format ); + format = codec_and_format; + codec_name = NULL; + char *f = strchr(codec_and_format, '/'); + if (f != NULL) + { + *f = 0; + codec_name = f+1; + } + } - SetupFormat( ); - SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); - SetParameters( ); - - // Allocate buffered packets. - packet_buffers = new AVPacket*[2]; - packet_buffers[0] = new AVPacket(); - packet_buffers[1] = new AVPacket(); - packet_index = 0; - - // Initialize mutex used by streaming thread. - if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) - { - Fatal("pthread_mutex_init failed"); - } + SetupFormat( ); + SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); + SetParameters( ); + + // Allocate buffered packets. + packet_buffers = new AVPacket*[2]; + packet_buffers[0] = new AVPacket(); + packet_buffers[1] = new AVPacket(); + packet_index = 0; + + // Initialize mutex used by streaming thread. + if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) + { + Fatal("pthread_mutex_init failed"); + } } VideoStream::~VideoStream( ) { - Debug( 1, "VideoStream destructor." ); - - // Stop streaming thread. - if ( streaming_thread ) - { - do_streaming = false; - void* thread_exit_code; + Debug( 1, "VideoStream destructor." ); + + // Stop streaming thread. + if ( streaming_thread ) + { + do_streaming = false; + void* thread_exit_code; + + Debug( 1, "Asking streaming thread to exit." ); + + // Wait for thread to exit. + pthread_join(streaming_thread, &thread_exit_code); + } + + if ( buffer_copy != NULL ) + { + av_free( buffer_copy ); + } - Debug( 1, "Asking streaming thread to exit." ); + if ( buffer_copy_lock ) + { + if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 ) + { + Error( "pthread_mutex_destroy failed" ); + } + delete buffer_copy_lock; + } - // Wait for thread to exit. - pthread_join(streaming_thread, &thread_exit_code); - } - - if ( buffer_copy != NULL ) - { - av_free( buffer_copy ); - } - - if ( buffer_copy_lock ) - { - if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 ) - { - Error( "pthread_mutex_destroy failed" ); + if (packet_buffers) { + delete packet_buffers[0]; + delete packet_buffers[1]; + delete[] packet_buffers; } - delete buffer_copy_lock; - } - - if (packet_buffers) { - delete packet_buffers[0]; - delete packet_buffers[1]; - delete[] packet_buffers; - } - - /* close each codec */ - if ( ost ) - { - avcodec_close( ost->codec ); - av_free( opicture->data[0] ); + + /* close each codec */ + if ( ost ) + { + avcodec_close( ost->codec ); + av_free( opicture->data[0] ); #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &opicture ); + av_frame_free( &opicture ); #else - av_freep( &opicture ); + av_freep( &opicture ); #endif - if ( tmp_opicture ) - { - av_free( tmp_opicture->data[0] ); + if ( tmp_opicture ) + { + av_free( tmp_opicture->data[0] ); #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &tmp_opicture ); + av_frame_free( &tmp_opicture ); #else - av_freep( &tmp_opicture ); + av_freep( &tmp_opicture ); #endif - } - av_free( video_outbuf ); - } + } + av_free( video_outbuf ); + } - /* write the trailer, if any */ - av_write_trailer( ofc ); + /* write the trailer, if any */ + av_write_trailer( ofc ); - /* free the streams */ - for ( unsigned int i = 0; i < ofc->nb_streams; i++ ) - { - av_freep( &ofc->streams[i] ); - } + /* free the streams */ + for ( unsigned int i = 0; i < ofc->nb_streams; i++ ) + { + av_freep( &ofc->streams[i] ); + } - if ( !(of->flags & AVFMT_NOFILE) ) - { - /* close the output file */ + if ( !(of->flags & AVFMT_NOFILE) ) + { + /* close the output file */ #if LIBAVFORMAT_VERSION_CHECK(52, 105, 0, 105, 0) - avio_close( ofc->pb ); + avio_close( ofc->pb ); #else - url_fclose( ofc->pb ); + url_fclose( ofc->pb ); #endif - } + } - /* free the stream */ - av_free( ofc ); - - /* free format and codec_name data. */ - if ( codec_and_format ) - { - delete codec_and_format; - } + /* free the stream */ + av_free( ofc ); + + /* free format and codec_name data. */ + if ( codec_and_format ) + { + delete codec_and_format; + } } double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _add_timestamp, unsigned int _timestamp ) { - if ( pthread_mutex_lock( buffer_copy_lock ) != 0 ) - { - Fatal( "EncodeFrame: pthread_mutex_lock failed." ); - } - - if (buffer_copy_size < buffer_size) - { - if ( buffer_copy ) - { - av_free( buffer_copy ); - } - - // Allocate a buffer to store source images for the streaming thread to encode. - buffer_copy = (uint8_t *)av_malloc( buffer_size ); - if ( !buffer_copy ) - { - Panic( "Could not allocate buffer_copy" ); - } - buffer_copy_size = buffer_size; - } - - add_timestamp = _add_timestamp; - timestamp = _timestamp; - buffer_copy_used = buffer_size; - memcpy(buffer_copy, buffer, buffer_size); - - if ( pthread_mutex_unlock( buffer_copy_lock ) != 0 ) - { - Fatal( "EncodeFrame: pthread_mutex_unlock failed." ); - } - - if ( streaming_thread == 0 ) - { - Debug( 1, "Starting streaming thread" ); - - // Start a thread for streaming encoded video. - if (pthread_create( &streaming_thread, NULL, StreamingThreadCallback, (void*) this) != 0){ - // Log a fatal error and exit the process. - Fatal( "VideoStream failed to create streaming thread." ); - } - } - - //return ActuallyEncodeFrame( buffer, buffer_size, add_timestamp, timestamp); - - return _timestamp; + if ( pthread_mutex_lock( buffer_copy_lock ) != 0 ) + { + Fatal( "EncodeFrame: pthread_mutex_lock failed." ); + } + + if (buffer_copy_size < buffer_size) + { + if ( buffer_copy ) + { + av_free( buffer_copy ); + } + + // Allocate a buffer to store source images for the streaming thread to encode. + buffer_copy = (uint8_t *)av_malloc( buffer_size ); + if ( !buffer_copy ) + { + Panic( "Could not allocate buffer_copy" ); + } + buffer_copy_size = buffer_size; + } + + add_timestamp = _add_timestamp; + timestamp = _timestamp; + buffer_copy_used = buffer_size; + memcpy(buffer_copy, buffer, buffer_size); + + if ( pthread_mutex_unlock( buffer_copy_lock ) != 0 ) + { + Fatal( "EncodeFrame: pthread_mutex_unlock failed." ); + } + + if ( streaming_thread == 0 ) + { + Debug( 1, "Starting streaming thread" ); + + // Start a thread for streaming encoded video. + if (pthread_create( &streaming_thread, NULL, StreamingThreadCallback, (void*) this) != 0){ + // Log a fatal error and exit the process. + Fatal( "VideoStream failed to create streaming thread." ); + } + } + + //return ActuallyEncodeFrame( buffer, buffer_size, add_timestamp, timestamp); + + return _timestamp; } double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) { #ifdef HAVE_LIBSWSCALE - static struct SwsContext *img_convert_ctx = 0; + static struct SwsContext *img_convert_ctx = 0; #endif // HAVE_LIBSWSCALE - AVCodecContext *c = ost->codec; + AVCodecContext *c = ost->codec; - if ( c->pix_fmt != pf ) - { - memcpy( tmp_opicture->data[0], buffer, buffer_size ); + if ( c->pix_fmt != pf ) + { + memcpy( tmp_opicture->data[0], buffer, buffer_size ); #ifdef HAVE_LIBSWSCALE - if ( !img_convert_ctx ) - { - img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); - if ( !img_convert_ctx ) - Panic( "Unable to initialise image scaling context" ); - } - sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); + if ( !img_convert_ctx ) + { + img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); + if ( !img_convert_ctx ) + Panic( "Unable to initialise image scaling context" ); + } + sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); #else // HAVE_LIBSWSCALE - Fatal( "swscale is required for MPEG mode" ); + Fatal( "swscale is required for MPEG mode" ); #endif // HAVE_LIBSWSCALE - } - else - { - memcpy( opicture->data[0], buffer, buffer_size ); - } - AVFrame *opicture_ptr = opicture; - - AVPacket *pkt = packet_buffers[packet_index]; - av_init_packet( pkt ); - int got_packet = 0; - if ( of->flags & AVFMT_RAWPICTURE ) - { + } + else + { + memcpy( opicture->data[0], buffer, buffer_size ); + } + AVFrame *opicture_ptr = opicture; + + AVPacket *pkt = packet_buffers[packet_index]; + av_init_packet( pkt ); + int got_packet = 0; + if ( of->flags & AVFMT_RAWPICTURE ) + { #if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) - pkt->flags |= AV_PKT_FLAG_KEY; + pkt->flags |= AV_PKT_FLAG_KEY; #else - pkt->flags |= PKT_FLAG_KEY; + pkt->flags |= PKT_FLAG_KEY; #endif - pkt->stream_index = ost->index; - pkt->data = (uint8_t *)opicture_ptr; - pkt->size = sizeof (AVPicture); - got_packet = 1; - } - else - { - opicture_ptr->pts = c->frame_number; - opicture_ptr->quality = c->global_quality; + pkt->stream_index = ost->index; + pkt->data = (uint8_t *)opicture_ptr; + pkt->size = sizeof (AVPicture); + got_packet = 1; + } + else + { + opicture_ptr->pts = c->frame_number; + opicture_ptr->quality = c->global_quality; #if LIBAVFORMAT_VERSION_CHECK(54, 1, 0, 2, 100) - int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet ); - if ( ret != 0 ) - { - Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) ); - } + int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet ); + if ( ret != 0 ) + { + Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) ); + } #else - int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr ); - got_packet = out_size > 0 ? 1 : 0; - pkt->data = got_packet ? video_outbuf : NULL; - pkt->size = got_packet ? out_size : 0; + int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr ); + got_packet = out_size > 0 ? 1 : 0; + pkt->data = got_packet ? video_outbuf : NULL; + pkt->size = got_packet ? out_size : 0; #endif - if ( got_packet ) - { - if ( c->coded_frame->key_frame ) - { -#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) - pkt->flags |= AV_PKT_FLAG_KEY; -#else - pkt->flags |= PKT_FLAG_KEY; -#endif - } + if ( got_packet ) + { +// if ( c->coded_frame->key_frame ) +// { +//#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) +// pkt->flags |= AV_PKT_FLAG_KEY; +//#else +// pkt->flags |= PKT_FLAG_KEY; +//#endif +// } - if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) - { - pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base ); - } - if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) - { - pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base ); - } - pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base ); - pkt->stream_index = ost->index; - } - } - - return ( opicture_ptr->pts); + if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) + { + pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base ); + } + if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) + { + pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base ); + } + pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base ); + pkt->stream_index = ost->index; + } + } + + return ( opicture_ptr->pts); } int VideoStream::SendPacket(AVPacket *packet) { - - int ret = av_write_frame( ofc, packet ); - if ( ret != 0 ) - { - Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) ); - } + + int ret = av_write_frame( ofc, packet ); + if ( ret != 0 ) + { + Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) ); + } #if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) - av_packet_unref( packet ); + av_packet_unref( packet ); #else - av_free_packet( packet ); + av_free_packet( packet ); #endif - return ret; + return ret; } void *VideoStream::StreamingThreadCallback(void *ctx){ - - Debug( 1, "StreamingThreadCallback started" ); - - if (ctx == NULL) return NULL; + + Debug( 1, "StreamingThreadCallback started" ); + + if (ctx == NULL) return NULL; - VideoStream* videoStream = reinterpret_cast(ctx); - - const uint64_t nanosecond_multiplier = 1000000000; - - uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); - uint64_t frame_count = 0; - timespec start_time; - clock_gettime(CLOCK_MONOTONIC, &start_time); - uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec; - while(videoStream->do_streaming) - { - timespec current_time; - clock_gettime(CLOCK_MONOTONIC, ¤t_time); - uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec; - uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count); - - if ( current_time_ns < target_ns ) - { - // It's not time to render a frame yet. - usleep( (target_ns - current_time_ns) * 0.001 ); - } - - // By sending the last rendered frame we deliver frames to the client more accurate. - // If we're encoding the frame before sending it there will be lag. - // Since this lag is not constant the client may skip frames. - - // Get the last rendered packet. - AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; - if (packet->size) { - videoStream->SendPacket(packet); - } + VideoStream* videoStream = reinterpret_cast(ctx); + + const uint64_t nanosecond_multiplier = 1000000000; + + uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); + uint64_t frame_count = 0; + timespec start_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec; + while(videoStream->do_streaming) + { + timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec; + uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count); + + if ( current_time_ns < target_ns ) + { + // It's not time to render a frame yet. + usleep( (target_ns - current_time_ns) * 0.001 ); + } + + // By sending the last rendered frame we deliver frames to the client more accurate. + // If we're encoding the frame before sending it there will be lag. + // Since this lag is not constant the client may skip frames. + + // Get the last rendered packet. + AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; + if (packet->size) { + videoStream->SendPacket(packet); + } #if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) - av_packet_unref( packet); + av_packet_unref( packet); #else - av_free_packet( packet ); + av_free_packet( packet ); #endif - videoStream->packet_index = videoStream->packet_index ? 0 : 1; - - // Lock buffer and render next frame. - - if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) - { - Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." ); - } - - if ( videoStream->buffer_copy ) - { - // Encode next frame. - videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp ); - } - - if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) - { - Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." ); - } - - frame_count++; - } - - return 0; + videoStream->packet_index = videoStream->packet_index ? 0 : 1; + + // Lock buffer and render next frame. + + if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) + { + Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." ); + } + + if ( videoStream->buffer_copy ) + { + // Encode next frame. + videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp ); + } + + if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) + { + Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." ); + } + + frame_count++; + } + + return 0; } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_remote_camera.cpp b/src/zm_remote_camera.cpp index b82589cd6..388607f6b 100644 --- a/src/zm_remote_camera.cpp +++ b/src/zm_remote_camera.cpp @@ -21,67 +21,82 @@ #include "zm_utils.h" -RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - protocol( p_protocol ), - host( p_host ), - port( p_port ), - path( p_path ), - hp( 0 ) +RemoteCamera::RemoteCamera( + int p_id, + const std::string &p_protocol, + const std::string &p_host, + const std::string &p_port, + const std::string &p_path, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio + ) : + Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + protocol( p_protocol ), + host( p_host ), + port( p_port ), + path( p_path ), + hp( 0 ) { - if ( path[0] != '/' ) - path = '/'+path; + if ( path[0] != '/' ) + path = '/'+path; } RemoteCamera::~RemoteCamera() { - if(hp != NULL) { - freeaddrinfo(hp); - hp = NULL; - } + if(hp != NULL) { + freeaddrinfo(hp); + hp = NULL; + } } void RemoteCamera::Initialise() { - if( protocol.empty() ) - Fatal( "No protocol specified for remote camera" ); + if( protocol.empty() ) + Fatal( "No protocol specified for remote camera" ); - if( host.empty() ) - Fatal( "No host specified for remote camera" ); + if( host.empty() ) + Fatal( "No host specified for remote camera" ); - if( port.empty() ) - Fatal( "No port specified for remote camera" ); + if( port.empty() ) + Fatal( "No port specified for remote camera" ); - //if( path.empty() ) - //Fatal( "No path specified for remote camera" ); + //if( path.empty() ) + //Fatal( "No path specified for remote camera" ); - // Cache as much as we can to speed things up - std::string::size_type authIndex = host.rfind( '@' ); + // Cache as much as we can to speed things up + std::string::size_type authIndex = host.rfind( '@' ); - if ( authIndex != std::string::npos ) - { - auth = host.substr( 0, authIndex ); - host.erase( 0, authIndex+1 ); - auth64 = base64Encode( auth ); + if ( authIndex != std::string::npos ) + { + auth = host.substr( 0, authIndex ); + host.erase( 0, authIndex+1 ); + auth64 = base64Encode( auth ); - authIndex = auth.rfind( ':' ); - username = auth.substr(0,authIndex); - password = auth.substr( authIndex+1, auth.length() ); + authIndex = auth.rfind( ':' ); + username = auth.substr(0,authIndex); + password = auth.substr( authIndex+1, auth.length() ); - } + } - mNeedAuth = false; - mAuthenticator = new zm::Authenticator(username,password); + mNeedAuth = false; + mAuthenticator = new zm::Authenticator(username,password); - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; - int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); - if ( ret != 0 ) - { - Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); - } + int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); + if ( ret != 0 ) + { + Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); + } } diff --git a/src/zm_remote_camera.h b/src/zm_remote_camera.h index 647782fd1..4caae411e 100644 --- a/src/zm_remote_camera.h +++ b/src/zm_remote_camera.h @@ -27,6 +27,7 @@ #include #include #include +#include // // Class representing 'remote' cameras, i.e. those which are @@ -35,44 +36,60 @@ class RemoteCamera : public Camera { protected: - std::string protocol; - std::string host; - std::string port; - std::string path; - std::string auth; - std::string username; - std::string password; - std::string auth64; + std::string protocol; + std::string host; + std::string port; + std::string path; + std::string auth; + std::string username; + std::string password; + std::string auth64; - // Reworked authentication system - // First try without authentication, even if we have a username and password - // on receiving a 401 response, select authentication method (basic or digest) - // fill required fields and set needAuth - // subsequent requests can set the required authentication header. - bool mNeedAuth; - zm::Authenticator* mAuthenticator; + // Reworked authentication system + // First try without authentication, even if we have a username and password + // on receiving a 401 response, select authentication method (basic or digest) + // fill required fields and set needAuth + // subsequent requests can set the required authentication header. + bool mNeedAuth; + zm::Authenticator* mAuthenticator; protected: - struct addrinfo *hp; + struct addrinfo *hp; public: - RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - virtual ~RemoteCamera(); + RemoteCamera( + int p_id, + const std::string &p_proto, + const std::string &p_host, + const std::string &p_port, + const std::string &p_path, + int p_width, + int p_height, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio + ); + virtual ~RemoteCamera(); - const std::string &Protocol() const { return( protocol ); } - const std::string &Host() const { return( host ); } - const std::string &Port() const { return( port ); } - const std::string &Path() const { return( path ); } - const std::string &Auth() const { return( auth ); } - const std::string &Username() const { return( username ); } - const std::string &Password() const { return( password ); } + const std::string &Protocol() const { return( protocol ); } + const std::string &Host() const { return( host ); } + const std::string &Port() const { return( port ); } + const std::string &Path() const { return( path ); } + const std::string &Auth() const { return( auth ); } + const std::string &Username() const { return( username ); } + const std::string &Password() const { return( password ); } - virtual void Initialise(); - virtual void Terminate() = 0; - virtual int Connect() = 0; - virtual int Disconnect() = 0; - virtual int PreCapture() = 0; - virtual int Capture( Image &image ) = 0; - virtual int PostCapture() = 0; + virtual void Initialise(); + virtual void Terminate() = 0; + virtual int Connect() = 0; + virtual int Disconnect() = 0; + virtual int PreCapture() = 0; + virtual int Capture( Image &image ) = 0; + virtual int PostCapture() = 0; + virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory )=0; }; #endif // ZM_REMOTE_CAMERA_H diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 9454b8d69..5babc0b0e 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -31,121 +31,126 @@ #include // FIONREAD and friends #endif -RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ) +RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ) { - sd = -1; + sd = -1; - timeout.tv_sec = 0; - timeout.tv_usec = 0; + timeout.tv_sec = 0; + timeout.tv_usec = 0; - if ( p_method == "simple" ) - method = SIMPLE; - else if ( p_method == "regexp" ) - method = REGEXP; - else - Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), id ); - if ( capture ) - { - Initialise(); - } + if ( p_method == "simple" ) + method = SIMPLE; + else if ( p_method == "regexp" ) + method = REGEXP; + else + Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), id ); + if ( capture ) + { + Initialise(); + } } RemoteCameraHttp::~RemoteCameraHttp() { - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void RemoteCameraHttp::Initialise() { - RemoteCamera::Initialise(); + RemoteCamera::Initialise(); - if ( request.empty() ) - { - request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); - request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); - request += stringtf( "Host: %s\r\n", host.c_str()); - if ( strcmp( config.http_version, "1.0" ) == 0 ) - request += stringtf( "Connection: Keep-Alive\r\n" ); - if ( !auth.empty() ) - request += stringtf( "Authorization: Basic %s\r\n", auth64.c_str() ); - request += "\r\n"; - Debug( 2, "Request: %s", request.c_str() ); - } + if ( request.empty() ) + { + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + if ( !auth.empty() ) + request += stringtf( "Authorization: Basic %s\r\n", auth64.c_str() ); + request += "\r\n"; + Debug( 2, "Request: %s", request.c_str() ); + } - if ( !timeout.tv_sec ) - { - timeout.tv_sec = config.http_timeout/1000; - timeout.tv_usec = (config.http_timeout%1000)*1000; - } + if ( !timeout.tv_sec ) + { + timeout.tv_sec = config.http_timeout/1000; + timeout.tv_usec = (config.http_timeout%1000)*1000; + } - int max_size = width*height*colours; + int max_size = width*height*colours; - buffer.size( max_size ); + buffer.size( max_size ); - mode = SINGLE_IMAGE; - format = UNDEF; - state = HEADER; + mode = SINGLE_IMAGE; + format = UNDEF; + state = HEADER; } int RemoteCameraHttp::Connect() { - struct addrinfo *p; + struct addrinfo *p; - for(p = hp; p != NULL; p = p->ai_next) - { - sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol ); - if ( sd < 0 ) + for(p = hp; p != NULL; p = p->ai_next) { - Warning("Can't create socket: %s", strerror(errno) ); - continue; + sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol ); + if ( sd < 0 ) + { + Warning("Can't create socket: %s", strerror(errno) ); + continue; + } + + if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) + { + close(sd); + sd = -1; + char buf[sizeof(struct in6_addr)]; + struct sockaddr_in *addr; + addr = (struct sockaddr_in *)p->ai_addr; + inet_ntop( AF_INET, &(addr->sin_addr), buf, INET6_ADDRSTRLEN ); + + Warning("Can't connect to remote camera mid: %d at %s: %s", id, buf, strerror(errno) ); + continue; + } + + /* If we got here, we must have connected successfully */ + break; } - if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) - { - close(sd); - sd = -1; - Warning("Can't connect to remote camera: %s", strerror(errno) ); - continue; + if(p == NULL) { + Error("Unable to connect to the remote camera, aborting"); + return( -1 ); } - /* If we got here, we must have connected successfully */ - break; - } - - if(p == NULL) { - Error("Unable to connect to the remote camera, aborting"); - return( -1 ); - } - - Debug( 3, "Connected to host, socket = %d", sd ); - return( sd ); + Debug( 3, "Connected to host, socket = %d", sd ); + return( sd ); } int RemoteCameraHttp::Disconnect() { - close( sd ); - sd = -1; - Debug( 3, "Disconnected from host" ); - return( 0 ); + close( sd ); + sd = -1; + Debug( 3, "Disconnected from host" ); + return( 0 ); } int RemoteCameraHttp::SendRequest() { - Debug( 2, "Sending request: %s", request.c_str() ); - if ( write( sd, request.data(), request.length() ) < 0 ) - { - Error( "Can't write: %s", strerror(errno) ); - Disconnect(); - return( -1 ); - } - format = UNDEF; - state = HEADER; - Debug( 3, "Request sent" ); - return( 0 ); + Debug( 2, "Sending request: %s", request.c_str() ); + if ( write( sd, request.data(), request.length() ) < 0 ) + { + Error( "Can't write: %s", strerror(errno) ); + Disconnect(); + return( -1 ); + } + format = UNDEF; + state = HEADER; + Debug( 3, "Request sent" ); + return( 0 ); } /* Return codes are as follows: @@ -156,1037 +161,1037 @@ int RemoteCameraHttp::SendRequest() int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected ) { - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(sd, &rfds); + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(sd, &rfds); - struct timeval temp_timeout = timeout; + struct timeval temp_timeout = timeout; - int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); - if( n_found == 0 ) - { - Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec ); - // Why are we disconnecting? It's just a timeout, meaning that data wasn't available. - //Disconnect(); - return( 0 ); - } - else if ( n_found < 0) - { - Error( "Select error: %s", strerror(errno) ); - return( -1 ); - } - - int total_bytes_to_read = 0; - - if ( bytes_expected ) - { - total_bytes_to_read = bytes_expected; - } - else - { - if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) + int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); + if( n_found == 0 ) { - Error( "Can't ioctl(): %s", strerror(errno) ); - return( -1 ); + Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec ); + // Why are we disconnecting? It's just a timeout, meaning that data wasn't available. + //Disconnect(); + return( 0 ); } - - if ( total_bytes_to_read == 0 ) + else if ( n_found < 0) { - // If socket is closed locally, then select will fail, but if it is closed remotely - // then we have an exception on our socket.. but no data. - Debug( 3, "Socket closed remotely" ); - //Disconnect(); // Disconnect is done outside of ReadData now. - return( -1 ); + Error( "Select error: %s", strerror(errno) ); + return( -1 ); } - // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily. - if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) { - total_bytes_to_read = ZM_NETWORK_BUFSIZ; - Debug(3, "Just getting 32K" ); - } else { - Debug(3, "Just getting %d", total_bytes_to_read ); - } - } - Debug( 3, "Expecting %d bytes", total_bytes_to_read ); + int total_bytes_to_read = 0; - int total_bytes_read = 0; - do - { - int bytes_read = buffer.read_into( sd, total_bytes_to_read ); - if ( bytes_read < 0) + if ( bytes_expected ) { - Error( "Read error: %s", strerror(errno) ); - return( -1 ); + total_bytes_to_read = bytes_expected; } - else if ( bytes_read == 0) + else { - Debug( 2, "Socket closed" ); - //Disconnect(); // Disconnect is done outside of ReadData now. - return( -1 ); - } - else if ( bytes_read < total_bytes_to_read ) + if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) + { + Error( "Can't ioctl(): %s", strerror(errno) ); + return( -1 ); + } + + if ( total_bytes_to_read == 0 ) + { + // If socket is closed locally, then select will fail, but if it is closed remotely + // then we have an exception on our socket.. but no data. + Debug( 3, "Socket closed remotely" ); + //Disconnect(); // Disconnect is done outside of ReadData now. + return( -1 ); + } + + // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily. + if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) { + total_bytes_to_read = ZM_NETWORK_BUFSIZ; + Debug(3, "Just getting 32K" ); + } else { + Debug(3, "Just getting %d", total_bytes_to_read ); + } + } + Debug( 3, "Expecting %d bytes", total_bytes_to_read ); + + int total_bytes_read = 0; + do { - Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read ); - return( -1 ); + int bytes_read = buffer.read_into( sd, total_bytes_to_read ); + if ( bytes_read < 0) + { + Error( "Read error: %s", strerror(errno) ); + return( -1 ); + } + else if ( bytes_read == 0) + { + Debug( 2, "Socket closed" ); + //Disconnect(); // Disconnect is done outside of ReadData now. + return( -1 ); + } + else if ( bytes_read < total_bytes_to_read ) + { + Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read ); + return( -1 ); + } + Debug( 3, "Read %d bytes", bytes_read ); + total_bytes_read += bytes_read; + total_bytes_to_read -= bytes_read; } - Debug( 3, "Read %d bytes", bytes_read ); - total_bytes_read += bytes_read; - total_bytes_to_read -= bytes_read; - } - while ( total_bytes_to_read ); + while ( total_bytes_to_read ); - Debug( 4, buffer ); + Debug( 4, buffer ); - return( total_bytes_read ); + return( total_bytes_read ); } int RemoteCameraHttp::GetResponse() { - int buffer_len; + int buffer_len; #if HAVE_LIBPCRE - if ( method == REGEXP ) - { - const char *header = 0; - int header_len = 0; - const char *http_version = 0; - int status_code = 0; - const char *status_mesg = 0; - const char *connection_type = ""; - int content_length = 0; - const char *content_type = ""; - const char *content_boundary = ""; - const char *subheader = 0; - int subheader_len = 0; - //int subcontent_length = 0; - //const char *subcontent_type = ""; - - while ( true ) - { - switch( state ) - { - case HEADER : - { - static RegExpr *header_expr = 0; - static RegExpr *status_expr = 0; - static RegExpr *connection_expr = 0; - static RegExpr *content_length_expr = 0; - static RegExpr *content_type_expr = 0; - - while ( ! ( buffer_len = ReadData( buffer ) ) ) { - } - if ( buffer_len < 0 ) { - Error( "Unable to read header data" ); - return( -1 ); - } - if ( !header_expr ) - header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); - if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) - { - header = header_expr->MatchString( 1 ); - header_len = header_expr->MatchLength( 1 ); - Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); - - if ( !status_expr ) - status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); - if ( status_expr->Match( header, header_len ) < 4 ) - { - Error( "Unable to extract HTTP status from header" ); - return( -1 ); - } - http_version = status_expr->MatchString( 1 ); - status_code = atoi( status_expr->MatchString( 2 ) ); - status_mesg = status_expr->MatchString( 3 ); - - if ( status_code == 401 ) { - if ( mNeedAuth ) { - Error( "Failed authentication: " ); - return( -1 ); - } - mNeedAuth = true; - std::string Header = header; - - mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { - Debug( 2, "Need Digest Authentication" ); - request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); - request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); - request += stringtf( "Host: %s\r\n", host.c_str()); - if ( strcmp( config.http_version, "1.0" ) == 0 ) - request += stringtf( "Connection: Keep-Alive\r\n" ); - request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); - request += "\r\n"; - - Debug( 2, "New request header: %s", request.c_str() ); - return( 0 ); - } - - } else if ( status_code < 200 || status_code > 299 ) { - Error( "Invalid response status %d: %s\n%s", status_code, status_mesg, (char *)buffer ); - return( -1 ); - } - Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); - - if ( !connection_expr ) - connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); - if ( connection_expr->Match( header, header_len ) == 2 ) - { - connection_type = connection_expr->MatchString( 1 ); - Debug( 3, "Got connection '%s'", connection_type ); - } - - if ( !content_length_expr ) - content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); - if ( content_length_expr->Match( header, header_len ) == 2 ) - { - content_length = atoi( content_length_expr->MatchString( 1 ) ); - Debug( 3, "Got content length '%d'", content_length ); - } - - if ( !content_type_expr ) - content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); - if ( content_type_expr->Match( header, header_len ) >= 2 ) - { - content_type = content_type_expr->MatchString( 1 ); - Debug( 3, "Got content type '%s'\n", content_type ); - if ( content_type_expr->MatchCount() > 2 ) - { - content_boundary = content_type_expr->MatchString( 2 ); - Debug( 3, "Got content boundary '%s'", content_boundary ); - } - } - - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = JPEG; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGB; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGBZ; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) - { - // Image stream, so start processing - if ( !content_boundary[0] ) - { - Error( "No content boundary found in header '%s'", header ); - return( -1 ); - } - mode = MULTI_IMAGE; - state = SUBHEADER; - } - //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) - //{ - //// MPEG stream, coming soon! - //} - else - { - Error( "Unrecognised content type '%s'", content_type ); - return( -1 ); - } - buffer.consume( header_len ); - } - else - { - Debug( 3, "Unable to extract header from stream, retrying" ); - //return( -1 ); - } - break; - } - case SUBHEADER : - { - static RegExpr *subheader_expr = 0; - static RegExpr *subcontent_length_expr = 0; - static RegExpr *subcontent_type_expr = 0; - - if ( !subheader_expr ) - { - char subheader_pattern[256] = ""; - snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); - subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); - } - if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) - { - subheader = subheader_expr->MatchString( 1 ); - subheader_len = subheader_expr->MatchLength( 1 ); - Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); - - if ( !subcontent_length_expr ) - subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); - if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) - { - content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); - Debug( 3, "Got subcontent length '%d'", content_length ); - } - - if ( !subcontent_type_expr ) - subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); - if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) - { - content_type = subcontent_type_expr->MatchString( 1 ); - Debug( 3, "Got subcontent type '%s'", content_type ); - } - - buffer.consume( subheader_len ); - state = CONTENT; - } - else - { - Debug( 3, "Unable to extract subheader from stream, retrying" ); - while ( ! ( buffer_len = ReadData( buffer ) ) ) { - } - if ( buffer_len < 0 ) { - Error( "Unable to extract subheader data" ); - return( -1 ); - } - } - break; - } - case CONTENT : - { - - // if content_type is something like image/jpeg;size=, this will strip the ;size= - char * semicolon = strchr( (char *)content_type, ';' ); - if ( semicolon ) { - *semicolon = '\0'; - } - - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - format = JPEG; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - format = X_RGB; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - format = X_RGBZ; - } - else - { - Error( "Found unsupported content type '%s'", content_type ); - return( -1 ); - } - - if ( content_length ) - { - while ( (long)buffer.size() < content_length ) - { -Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); - if ( ReadData( buffer ) < 0 ) { - Error( "Unable to read content" ); - return( -1 ); - } - } - Debug( 3, "Got end of image by length, content-length = %d", content_length ); - } - else - { - while ( !content_length ) - { - while ( ! ( buffer_len = ReadData( buffer ) ) ) { - } - if ( buffer_len < 0 ) { - Error( "Unable to read content" ); - return( -1 ); - } - static RegExpr *content_expr = 0; - if ( mode == MULTI_IMAGE ) - { - if ( !content_expr ) - { - char content_pattern[256] = ""; - snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); - content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); - } - if ( content_expr->Match( buffer, buffer.size() ) == 2 ) - { - content_length = content_expr->MatchLength( 1 ); - Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); - } - } - } - } - if ( mode == SINGLE_IMAGE ) - { - state = HEADER; - Disconnect(); - } - else - { - state = SUBHEADER; - } - Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); - return( content_length ); - } - case HEADERCONT : - case SUBHEADERCONT : - { - // Ignore - break; - } - } - } - } - else -#endif // HAVE_LIBPCRE - { if ( method == REGEXP ) { - Warning( "Unable to use netcam regexps as not compiled with libpcre" ); - } - static const char *http_match = "HTTP/"; - static const char *connection_match = "Connection:"; - static const char *content_length_match = "Content-length:"; - static const char *content_type_match = "Content-type:"; - static const char *boundary_match = "boundary="; - static const char *authenticate_match = "WWW-Authenticate:"; - static int http_match_len = 0; - static int connection_match_len = 0; - static int content_length_match_len = 0; - static int content_type_match_len = 0; - static int boundary_match_len = 0; - static int authenticate_match_len = 0; + const char *header = 0; + int header_len = 0; + const char *http_version = 0; + int status_code = 0; + const char *status_mesg = 0; + const char *connection_type = ""; + int content_length = 0; + const char *content_type = ""; + const char *content_boundary = ""; + const char *subheader = 0; + int subheader_len = 0; + //int subcontent_length = 0; + //const char *subcontent_type = ""; - if ( !http_match_len ) - http_match_len = strlen( http_match ); - if ( !connection_match_len ) - connection_match_len = strlen( connection_match ); - if ( !content_length_match_len ) - content_length_match_len = strlen( content_length_match ); - if ( !content_type_match_len ) - content_type_match_len = strlen( content_type_match ); - if ( !boundary_match_len ) - boundary_match_len = strlen( boundary_match ); - if ( !authenticate_match_len ) - authenticate_match_len = strlen( authenticate_match ); - - static int n_headers; - //static char *headers[32]; - - static int n_subheaders; - //static char *subheaders[32]; - - static char *http_header; - static char *connection_header; - static char *content_length_header; - static char *content_type_header; - static char *boundary_header; - static char *authenticate_header; - static char subcontent_length_header[32]; - static char subcontent_type_header[64]; - - static char http_version[16]; - static char status_code[16]; - static char status_mesg[256]; - static char connection_type[32]; - static int content_length; - static char content_type[32]; - static char content_boundary[64]; - static int content_boundary_len; - - while ( true ) - { - switch( state ) - { - case HEADER : + while ( true ) { - n_headers = 0; - http_header = 0; - connection_header = 0; - content_length_header = 0; - content_type_header = 0; - authenticate_header = 0; - - http_version[0] = '\0'; - status_code [0]= '\0'; - status_mesg [0]= '\0'; - connection_type [0]= '\0'; - content_length = 0; - content_type[0] = '\0'; - content_boundary[0] = '\0'; - content_boundary_len = 0; - } - case HEADERCONT : - { - while ( ! ( buffer_len = ReadData( buffer ) ) ) { - } - if ( buffer_len < 0 ) { - Error( "Unable to read header" ); - return( -1 ); - } - - char *crlf = 0; - char *header_ptr = (char *)buffer; - int header_len = buffer.size(); - bool all_headers = false; - - while( true ) - { - int crlf_len = memspn( header_ptr, "\r\n", header_len ); - if ( n_headers ) + switch( state ) { - if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) - { - *header_ptr = '\0'; - header_ptr += crlf_len; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - all_headers = true; - break; - } - } - if ( crlf_len ) - { - if ( header_len == crlf_len ) - { - break; - } - else - { - *header_ptr = '\0'; - header_ptr += crlf_len; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - } - } - - Debug( 6, "%s", header_ptr ); - if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) - { - //headers[n_headers++] = header_ptr; - n_headers++; - - if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) - { - http_header = header_ptr+http_match_len; - Debug( 6, "Got http header '%s'", header_ptr ); - } - else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) - { - connection_header = header_ptr+connection_match_len; - Debug( 6, "Got connection header '%s'", header_ptr ); - } - else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) - { - content_length_header = header_ptr+content_length_match_len; - Debug( 6, "Got content length header '%s'", header_ptr ); - } - - else if ( !authenticate_header && (strncasecmp( header_ptr, authenticate_match, authenticate_match_len) == 0) ) - { - authenticate_header = header_ptr; - Debug( 6, "Got authenticate header '%s'", header_ptr ); - } - else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) - { - content_type_header = header_ptr+content_type_match_len; - Debug( 6, "Got content type header '%s'", header_ptr ); - } - else - { - Debug( 6, "Got ignored header '%s'", header_ptr ); - } - header_ptr = crlf; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - } - else - { - // No end of line found - break; - } - } - - if ( all_headers ) - { - char *start_ptr, *end_ptr; - - if ( !http_header ) - { - Error( "Unable to extract HTTP status from header" ); - return( -1 ); - } - - start_ptr = http_header; - end_ptr = start_ptr+strspn( start_ptr, "10." ); - - memset( http_version, 0, sizeof(http_version) ); - strncpy( http_version, start_ptr, end_ptr-start_ptr ); - - start_ptr = end_ptr; - start_ptr += strspn( start_ptr, " " ); - end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); - - memset( status_code, 0, sizeof(status_code) ); - strncpy( status_code, start_ptr, end_ptr-start_ptr ); - int status = atoi( status_code ); - - start_ptr = end_ptr; - start_ptr += strspn( start_ptr, " " ); - strcpy( status_mesg, start_ptr ); - - if ( status == 401 ) { - if ( mNeedAuth ) { - Error( "Failed authentication: " ); - return( -1 ); - } - if ( ! authenticate_header ) { - Error( "Failed authentication, but don't have an authentication header: " ); - return( -1 ); - } - mNeedAuth = true; - std::string Header = authenticate_header; - Debug(2, "Checking for digest auth in %s", authenticate_header ); - - mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { - Debug( 2, "Need Digest Authentication" ); - request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); - request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); - request += stringtf( "Host: %s\r\n", host.c_str()); - if ( strcmp( config.http_version, "1.0" ) == 0 ) - request += stringtf( "Connection: Keep-Alive\r\n" ); - request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); - request += "\r\n"; - - Debug( 2, "New request header: %s", request.c_str() ); - return( 0 ); - } else { - Debug( 2, "Need some other kind of Authentication" ); - } - } else if ( status < 200 || status > 299 ) - { - Error( "Invalid response status %s: %s", status_code, status_mesg ); - return( -1 ); - } - Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); - - if ( connection_header ) - { - memset( connection_type, 0, sizeof(connection_type) ); - start_ptr = connection_header + strspn( connection_header, " " ); - strcpy( connection_type, start_ptr ); - Debug( 3, "Got connection '%s'", connection_type ); - } - if ( content_length_header ) - { - start_ptr = content_length_header + strspn( content_length_header, " " ); - content_length = atoi( start_ptr ); - Debug( 3, "Got content length '%d'", content_length ); - } - if ( content_type_header ) - { - memset( content_type, 0, sizeof(content_type) ); - start_ptr = content_type_header + strspn( content_type_header, " " ); - if ( (end_ptr = strchr( start_ptr, ';' )) ) - { - strncpy( content_type, start_ptr, end_ptr-start_ptr ); - Debug( 3, "Got content type '%s'", content_type ); - - start_ptr = end_ptr + strspn( end_ptr, "; " ); - - if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) + case HEADER : { - start_ptr += boundary_match_len; - start_ptr += strspn( start_ptr, "-" ); - content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); - Debug( 3, "Got content boundary '%s'", content_boundary ); - } - else - { - Error( "No content boundary found in header '%s'", content_type_header ); - } - } - else - { - strcpy( content_type, start_ptr ); - Debug( 3, "Got content type '%s'", content_type ); - } - } + static RegExpr *header_expr = 0; + static RegExpr *status_expr = 0; + static RegExpr *connection_expr = 0; + static RegExpr *content_length_expr = 0; + static RegExpr *content_type_expr = 0; - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = JPEG; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGB; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGBZ; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) - { - // Image stream, so start processing - if ( !content_boundary[0] ) - { - Error( "No content boundary found in header '%s'", content_type_header ); - return( -1 ); - } - mode = MULTI_IMAGE; - state = SUBHEADER; - } - //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) - //{ - //// MPEG stream, coming soon! - //} - else - { - Error( "Unrecognised content type '%s'", content_type ); - return( -1 ); - } - } - else - { - Debug( 3, "Unable to extract entire header from stream, continuing" ); - state = HEADERCONT; - //return( -1 ); - } - break; - } - case SUBHEADER : - { - n_subheaders = 0; - boundary_header = 0; - subcontent_length_header[0] = '\0'; - subcontent_type_header[0] = '\0'; - content_length = 0; - content_type[0] = '\0'; - } - case SUBHEADERCONT : - { - char *crlf = 0; - char *subheader_ptr = (char *)buffer; - int subheader_len = buffer.size(); - bool all_headers = false; + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read header data" ); + return( -1 ); + } + if ( !header_expr ) + header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); + if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) + { + header = header_expr->MatchString( 1 ); + header_len = header_expr->MatchLength( 1 ); + Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); - while( true ) - { - int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); - if ( n_subheaders ) - { - if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) - { - *subheader_ptr = '\0'; - subheader_ptr += crlf_len; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - all_headers = true; - break; - } - } - if ( crlf_len ) - { - if ( subheader_len == crlf_len ) - { - break; - } - else - { - *subheader_ptr = '\0'; - subheader_ptr += crlf_len; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - } - } + if ( !status_expr ) + status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); + if ( status_expr->Match( header, header_len ) < 4 ) + { + Error( "Unable to extract HTTP status from header" ); + return( -1 ); + } + http_version = status_expr->MatchString( 1 ); + status_code = atoi( status_expr->MatchString( 2 ) ); + status_mesg = status_expr->MatchString( 3 ); - Debug( 6, "%d: %s", subheader_len, subheader_ptr ); + if ( status_code == 401 ) { + if ( mNeedAuth ) { + Error( "Failed authentication: " ); + return( -1 ); + } + mNeedAuth = true; + std::string Header = header; + + mAuthenticator->checkAuthResponse(Header); + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { + Debug( 2, "Need Digest Authentication" ); + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); + request += "\r\n"; + + Debug( 2, "New request header: %s", request.c_str() ); + return( 0 ); + } - if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) - { - //subheaders[n_subheaders++] = subheader_ptr; - n_subheaders++; + } else if ( status_code < 200 || status_code > 299 ) { + Error( "Invalid response status %d: %s\n%s", status_code, status_mesg, (char *)buffer ); + return( -1 ); + } + Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); - if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) - { - boundary_header = subheader_ptr; - Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); - } - else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) - { - strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); - *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; - Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); - } - else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) - { - strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); - *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; - Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); - } - else - { - Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); - } - subheader_ptr = crlf; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - } - else - { - // No line end found - break; - } - } - - if ( all_headers && boundary_header ) - { - char *start_ptr/*, *end_ptr*/; + if ( !connection_expr ) + connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); + if ( connection_expr->Match( header, header_len ) == 2 ) + { + connection_type = connection_expr->MatchString( 1 ); + Debug( 3, "Got connection '%s'", connection_type ); + } - Debug( 3, "Got boundary '%s'", boundary_header ); + if ( !content_length_expr ) + content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); + if ( content_length_expr->Match( header, header_len ) == 2 ) + { + content_length = atoi( content_length_expr->MatchString( 1 ) ); + Debug( 3, "Got content length '%d'", content_length ); + } - if ( subcontent_length_header[0] ) - { - start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); - content_length = atoi( start_ptr ); - Debug( 3, "Got subcontent length '%d'", content_length ); - } - if ( subcontent_type_header[0] ) - { - memset( content_type, 0, sizeof(content_type) ); - start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); - strcpy( content_type, start_ptr ); - Debug( 3, "Got subcontent type '%s'", content_type ); - } - state = CONTENT; - } - else - { - Debug( 3, "Unable to extract subheader from stream, retrying" ); - while ( ! ( buffer_len = ReadData( buffer ) ) ) { - } - if ( buffer_len < 0 ) { - Error( "Unable to read subheader" ); - return( -1 ); - } - state = SUBHEADERCONT; - } - break; - } - case CONTENT : - { + if ( !content_type_expr ) + content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); + if ( content_type_expr->Match( header, header_len ) >= 2 ) + { + content_type = content_type_expr->MatchString( 1 ); + Debug( 3, "Got content type '%s'\n", content_type ); + if ( content_type_expr->MatchCount() > 2 ) + { + content_boundary = content_type_expr->MatchString( 2 ); + Debug( 3, "Got content boundary '%s'", content_boundary ); + } + } - // if content_type is something like image/jpeg;size=, this will strip the ;size= - char * semicolon = strchr( content_type, ';' ); - if ( semicolon ) { - *semicolon = '\0'; - } - - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - format = JPEG; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - format = X_RGB; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - format = X_RGBZ; - } - else - { - Error( "Found unsupported content type '%s'", content_type ); - return( -1 ); - } - - if ( format == JPEG && buffer.size() >= 2 ) - { - if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) - { - Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); - return( -1 ); - } - } - - if ( content_length ) - { - while ( (long)buffer.size() < content_length ) - { - //int buffer_len = ReadData( buffer, content_length-buffer.size() ); - if ( ReadData( buffer ) < 0 ) { - Error( "Unable to read content" ); - return( -1 ); - } - } - Debug( 3, "Got end of image by length, content-length = %d", content_length ); - } - else - { - int content_pos = 0; - while ( !content_length ) - { - buffer_len = ReadData( buffer ); - if ( buffer_len < 0 ) - { - Error( "Unable to read content" ); - return( -1 ); - } - int buffer_size = buffer.size(); - if ( buffer_len ) - { - if ( mode == MULTI_IMAGE ) - { - while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) - { - content_length = start_ptr - (char *)buffer; - Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = JPEG; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGB; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGBZ; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) + { + // Image stream, so start processing + if ( !content_boundary[0] ) + { + Error( "No content boundary found in header '%s'", header ); + return( -1 ); + } + mode = MULTI_IMAGE; + state = SUBHEADER; + } + //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) + //{ + //// MPEG stream, coming soon! + //} + else + { + Error( "Unrecognised content type '%s'", content_type ); + return( -1 ); + } + buffer.consume( header_len ); + } + else + { + Debug( 3, "Unable to extract header from stream, retrying" ); + //return( -1 ); + } break; - } } - } - else - { - content_length = buffer_size; - Debug( 3, "Got end of image by closure, content-length = %d", content_length ); - if ( mode == SINGLE_IMAGE ) + case SUBHEADER : { - char *end_ptr = (char *)buffer+buffer_size; + static RegExpr *subheader_expr = 0; + static RegExpr *subcontent_length_expr = 0; + static RegExpr *subcontent_type_expr = 0; - while( *end_ptr == '\r' || *end_ptr == '\n' ) - { - content_length--; - end_ptr--; - } + if ( !subheader_expr ) + { + char subheader_pattern[256] = ""; + snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); + subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); + } + if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) + { + subheader = subheader_expr->MatchString( 1 ); + subheader_len = subheader_expr->MatchLength( 1 ); + Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); - if ( end_ptr != ((char *)buffer+buffer_size) ) - { - Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); - } + if ( !subcontent_length_expr ) + subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); + if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) + { + content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); + Debug( 3, "Got subcontent length '%d'", content_length ); + } + + if ( !subcontent_type_expr ) + subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); + if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) + { + content_type = subcontent_type_expr->MatchString( 1 ); + Debug( 3, "Got subcontent type '%s'", content_type ); + } + + buffer.consume( subheader_len ); + state = CONTENT; + } + else + { + Debug( 3, "Unable to extract subheader from stream, retrying" ); + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to extract subheader data" ); + return( -1 ); + } + } + break; } - } - } - } - if ( mode == SINGLE_IMAGE ) - { - state = HEADER; - Disconnect(); - } - else - { - state = SUBHEADER; - } + case CONTENT : + { - if ( format == JPEG && buffer.size() >= 2 ) - { - if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) - { - Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); - return( -1 ); - } - } + // if content_type is something like image/jpeg;size=, this will strip the ;size= + char * semicolon = strchr( (char *)content_type, ';' ); + if ( semicolon ) { + *semicolon = '\0'; + } - Debug( 3, "Returning %d bytes, buffer size: (%d) bytes of captured content", content_length, buffer.size() ); - return( content_length ); + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + format = JPEG; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + format = X_RGB; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + format = X_RGBZ; + } + else + { + Error( "Found unsupported content type '%s'", content_type ); + return( -1 ); + } + + if ( content_length ) + { + while ( (long)buffer.size() < content_length ) + { +Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); + if ( ReadData( buffer ) < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + } + Debug( 3, "Got end of image by length, content-length = %d", content_length ); + } + else + { + while ( !content_length ) + { + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + static RegExpr *content_expr = 0; + if ( mode == MULTI_IMAGE ) + { + if ( !content_expr ) + { + char content_pattern[256] = ""; + snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); + content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); + } + if ( content_expr->Match( buffer, buffer.size() ) == 2 ) + { + content_length = content_expr->MatchLength( 1 ); + Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); + } + } + } + } + if ( mode == SINGLE_IMAGE ) + { + state = HEADER; + Disconnect(); + } + else + { + state = SUBHEADER; + } + Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); + return( content_length ); + } + case HEADERCONT : + case SUBHEADERCONT : + { + // Ignore + break; + } + } } - } } - } - return( 0 ); + else +#endif // HAVE_LIBPCRE + { + if ( method == REGEXP ) + { + Warning( "Unable to use netcam regexps as not compiled with libpcre" ); + } + static const char *http_match = "HTTP/"; + static const char *connection_match = "Connection:"; + static const char *content_length_match = "Content-length:"; + static const char *content_type_match = "Content-type:"; + static const char *boundary_match = "boundary="; + static const char *authenticate_match = "WWW-Authenticate:"; + static int http_match_len = 0; + static int connection_match_len = 0; + static int content_length_match_len = 0; + static int content_type_match_len = 0; + static int boundary_match_len = 0; + static int authenticate_match_len = 0; + + if ( !http_match_len ) + http_match_len = strlen( http_match ); + if ( !connection_match_len ) + connection_match_len = strlen( connection_match ); + if ( !content_length_match_len ) + content_length_match_len = strlen( content_length_match ); + if ( !content_type_match_len ) + content_type_match_len = strlen( content_type_match ); + if ( !boundary_match_len ) + boundary_match_len = strlen( boundary_match ); + if ( !authenticate_match_len ) + authenticate_match_len = strlen( authenticate_match ); + + static int n_headers; + //static char *headers[32]; + + static int n_subheaders; + //static char *subheaders[32]; + + static char *http_header; + static char *connection_header; + static char *content_length_header; + static char *content_type_header; + static char *boundary_header; + static char *authenticate_header; + static char subcontent_length_header[32]; + static char subcontent_type_header[64]; + + static char http_version[16]; + static char status_code[16]; + static char status_mesg[256]; + static char connection_type[32]; + static int content_length; + static char content_type[32]; + static char content_boundary[64]; + static int content_boundary_len; + + while ( true ) + { + switch( state ) + { + case HEADER : + { + n_headers = 0; + http_header = 0; + connection_header = 0; + content_length_header = 0; + content_type_header = 0; + authenticate_header = 0; + + http_version[0] = '\0'; + status_code [0]= '\0'; + status_mesg [0]= '\0'; + connection_type [0]= '\0'; + content_length = 0; + content_type[0] = '\0'; + content_boundary[0] = '\0'; + content_boundary_len = 0; + } + case HEADERCONT : + { + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read header" ); + return( -1 ); + } + + char *crlf = 0; + char *header_ptr = (char *)buffer; + int header_len = buffer.size(); + bool all_headers = false; + + while( true ) + { + int crlf_len = memspn( header_ptr, "\r\n", header_len ); + if ( n_headers ) + { + if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) + { + *header_ptr = '\0'; + header_ptr += crlf_len; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + all_headers = true; + break; + } + } + if ( crlf_len ) + { + if ( header_len == crlf_len ) + { + break; + } + else + { + *header_ptr = '\0'; + header_ptr += crlf_len; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + } + } + + Debug( 6, "%s", header_ptr ); + if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) + { + //headers[n_headers++] = header_ptr; + n_headers++; + + if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) + { + http_header = header_ptr+http_match_len; + Debug( 6, "Got http header '%s'", header_ptr ); + } + else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) + { + connection_header = header_ptr+connection_match_len; + Debug( 6, "Got connection header '%s'", header_ptr ); + } + else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) + { + content_length_header = header_ptr+content_length_match_len; + Debug( 6, "Got content length header '%s'", header_ptr ); + } + + else if ( !authenticate_header && (strncasecmp( header_ptr, authenticate_match, authenticate_match_len) == 0) ) + { + authenticate_header = header_ptr; + Debug( 6, "Got authenticate header '%s'", header_ptr ); + } + else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) + { + content_type_header = header_ptr+content_type_match_len; + Debug( 6, "Got content type header '%s'", header_ptr ); + } + else + { + Debug( 6, "Got ignored header '%s'", header_ptr ); + } + header_ptr = crlf; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + } + else + { + // No end of line found + break; + } + } + + if ( all_headers ) + { + char *start_ptr, *end_ptr; + + if ( !http_header ) + { + Error( "Unable to extract HTTP status from header" ); + return( -1 ); + } + + start_ptr = http_header; + end_ptr = start_ptr+strspn( start_ptr, "10." ); + + memset( http_version, 0, sizeof(http_version) ); + strncpy( http_version, start_ptr, end_ptr-start_ptr ); + + start_ptr = end_ptr; + start_ptr += strspn( start_ptr, " " ); + end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); + + memset( status_code, 0, sizeof(status_code) ); + strncpy( status_code, start_ptr, end_ptr-start_ptr ); + int status = atoi( status_code ); + + start_ptr = end_ptr; + start_ptr += strspn( start_ptr, " " ); + strcpy( status_mesg, start_ptr ); + + if ( status == 401 ) { + if ( mNeedAuth ) { + Error( "Failed authentication: " ); + return( -1 ); + } + if ( ! authenticate_header ) { + Error( "Failed authentication, but don't have an authentication header: " ); + return( -1 ); + } + mNeedAuth = true; + std::string Header = authenticate_header; + Debug(2, "Checking for digest auth in %s", authenticate_header ); + + mAuthenticator->checkAuthResponse(Header); + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { + Debug( 2, "Need Digest Authentication" ); + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); + request += "\r\n"; + + Debug( 2, "New request header: %s", request.c_str() ); + return( 0 ); + } else { + Debug( 2, "Need some other kind of Authentication" ); + } + } else if ( status < 200 || status > 299 ) + { + Error( "Invalid response status %s: %s", status_code, status_mesg ); + return( -1 ); + } + Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); + + if ( connection_header ) + { + memset( connection_type, 0, sizeof(connection_type) ); + start_ptr = connection_header + strspn( connection_header, " " ); + strcpy( connection_type, start_ptr ); + Debug( 3, "Got connection '%s'", connection_type ); + } + if ( content_length_header ) + { + start_ptr = content_length_header + strspn( content_length_header, " " ); + content_length = atoi( start_ptr ); + Debug( 3, "Got content length '%d'", content_length ); + } + if ( content_type_header ) + { + memset( content_type, 0, sizeof(content_type) ); + start_ptr = content_type_header + strspn( content_type_header, " " ); + if ( (end_ptr = strchr( start_ptr, ';' )) ) + { + strncpy( content_type, start_ptr, end_ptr-start_ptr ); + Debug( 3, "Got content type '%s'", content_type ); + + start_ptr = end_ptr + strspn( end_ptr, "; " ); + + if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) + { + start_ptr += boundary_match_len; + start_ptr += strspn( start_ptr, "-" ); + content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); + Debug( 3, "Got content boundary '%s'", content_boundary ); + } + else + { + Error( "No content boundary found in header '%s'", content_type_header ); + } + } + else + { + strcpy( content_type, start_ptr ); + Debug( 3, "Got content type '%s'", content_type ); + } + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = JPEG; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGB; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGBZ; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) + { + // Image stream, so start processing + if ( !content_boundary[0] ) + { + Error( "No content boundary found in header '%s'", content_type_header ); + return( -1 ); + } + mode = MULTI_IMAGE; + state = SUBHEADER; + } + //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) + //{ + //// MPEG stream, coming soon! + //} + else + { + Error( "Unrecognised content type '%s'", content_type ); + return( -1 ); + } + } + else + { + Debug( 3, "Unable to extract entire header from stream, continuing" ); + state = HEADERCONT; + //return( -1 ); + } + break; + } + case SUBHEADER : + { + n_subheaders = 0; + boundary_header = 0; + subcontent_length_header[0] = '\0'; + subcontent_type_header[0] = '\0'; + content_length = 0; + content_type[0] = '\0'; + } + case SUBHEADERCONT : + { + char *crlf = 0; + char *subheader_ptr = (char *)buffer; + int subheader_len = buffer.size(); + bool all_headers = false; + + while( true ) + { + int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); + if ( n_subheaders ) + { + if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) + { + *subheader_ptr = '\0'; + subheader_ptr += crlf_len; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + all_headers = true; + break; + } + } + if ( crlf_len ) + { + if ( subheader_len == crlf_len ) + { + break; + } + else + { + *subheader_ptr = '\0'; + subheader_ptr += crlf_len; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + } + } + + Debug( 6, "%d: %s", subheader_len, subheader_ptr ); + + if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) + { + //subheaders[n_subheaders++] = subheader_ptr; + n_subheaders++; + + if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) + { + boundary_header = subheader_ptr; + Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); + } + else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) + { + strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); + *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; + Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); + } + else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) + { + strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); + *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; + Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); + } + else + { + Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); + } + subheader_ptr = crlf; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + } + else + { + // No line end found + break; + } + } + + if ( all_headers && boundary_header ) + { + char *start_ptr/*, *end_ptr*/; + + Debug( 3, "Got boundary '%s'", boundary_header ); + + if ( subcontent_length_header[0] ) + { + start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); + content_length = atoi( start_ptr ); + Debug( 3, "Got subcontent length '%d'", content_length ); + } + if ( subcontent_type_header[0] ) + { + memset( content_type, 0, sizeof(content_type) ); + start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); + strcpy( content_type, start_ptr ); + Debug( 3, "Got subcontent type '%s'", content_type ); + } + state = CONTENT; + } + else + { + Debug( 3, "Unable to extract subheader from stream, retrying" ); + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read subheader" ); + return( -1 ); + } + state = SUBHEADERCONT; + } + break; + } + case CONTENT : + { + + // if content_type is something like image/jpeg;size=, this will strip the ;size= + char * semicolon = strchr( content_type, ';' ); + if ( semicolon ) { + *semicolon = '\0'; + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + format = JPEG; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + format = X_RGB; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + format = X_RGBZ; + } + else + { + Error( "Found unsupported content type '%s'", content_type ); + return( -1 ); + } + + if ( format == JPEG && buffer.size() >= 2 ) + { + if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) + { + Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); + return( -1 ); + } + } + + if ( content_length ) + { + while ( (long)buffer.size() < content_length ) + { + //int buffer_len = ReadData( buffer, content_length-buffer.size() ); + if ( ReadData( buffer ) < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + } + Debug( 3, "Got end of image by length, content-length = %d", content_length ); + } + else + { + int content_pos = 0; + while ( !content_length ) + { + buffer_len = ReadData( buffer ); + if ( buffer_len < 0 ) + { + Error( "Unable to read content" ); + return( -1 ); + } + int buffer_size = buffer.size(); + if ( buffer_len ) + { + if ( mode == MULTI_IMAGE ) + { + while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) + { + content_length = start_ptr - (char *)buffer; + Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); + break; + } + } + } + else + { + content_length = buffer_size; + Debug( 3, "Got end of image by closure, content-length = %d", content_length ); + if ( mode == SINGLE_IMAGE ) + { + char *end_ptr = (char *)buffer+buffer_size; + + while( *end_ptr == '\r' || *end_ptr == '\n' ) + { + content_length--; + end_ptr--; + } + + if ( end_ptr != ((char *)buffer+buffer_size) ) + { + Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); + } + } + } + } + } + if ( mode == SINGLE_IMAGE ) + { + state = HEADER; + Disconnect(); + } + else + { + state = SUBHEADER; + } + + if ( format == JPEG && buffer.size() >= 2 ) + { + if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) + { + Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); + return( -1 ); + } + } + + Debug( 3, "Returning %d bytes, buffer size: (%d) bytes of captured content", content_length, buffer.size() ); + return( content_length ); + } + } + } + } + return( 0 ); } int RemoteCameraHttp::PreCapture() { - if ( sd < 0 ) - { - Connect(); if ( sd < 0 ) { - Error( "Unable to connect to camera" ); - return( -1 ); + Connect(); + if ( sd < 0 ) + { + Error( "Unable to connect to camera" ); + return( -1 ); + } + mode = SINGLE_IMAGE; + buffer.clear(); } - mode = SINGLE_IMAGE; - buffer.clear(); - } - if ( mode == SINGLE_IMAGE ) - { - if ( SendRequest() < 0 ) + if ( mode == SINGLE_IMAGE ) { - Error( "Unable to send request" ); - Disconnect(); - return( -1 ); + if ( SendRequest() < 0 ) + { + Error( "Unable to send request" ); + Disconnect(); + return( -1 ); + } } - } - return( 0 ); + return( 0 ); } int RemoteCameraHttp::Capture( Image &image ) { - int content_length = GetResponse(); - if ( content_length == 0 ) - { - Warning( "Unable to capture image, retrying" ); - return( 1 ); - } - if ( content_length < 0 ) - { - Error( "Unable to get response, disconnecting" ); - Disconnect(); - return( -1 ); - } - switch( format ) - { - case JPEG : + int content_length = GetResponse(); + if ( content_length == 0 ) { - if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) - { - Error( "Unable to decode jpeg" ); + Warning( "Unable to capture image, retrying" ); + return( 1 ); + } + if ( content_length < 0 ) + { + Error( "Unable to get response, disconnecting" ); Disconnect(); return( -1 ); - } - break; } - case X_RGB : + switch( format ) { - if ( content_length != (long)image.Size() ) - { - Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length ); - Disconnect(); - return( -1 ); - } - image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); - break; + case JPEG : + { + if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) + { + Error( "Unable to decode jpeg" ); + Disconnect(); + return( -1 ); + } + break; + } + case X_RGB : + { + if ( content_length != (long)image.Size() ) + { + Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length ); + Disconnect(); + return( -1 ); + } + image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); + break; + } + case X_RGBZ : + { + if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) + { + Error( "Unable to unzip RGB image" ); + Disconnect(); + return( -1 ); + } + image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); + break; + } + default : + { + Error( "Unexpected image format encountered" ); + Disconnect(); + return( -1 ); + } } - case X_RGBZ : - { - if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) - { - Error( "Unable to unzip RGB image" ); - Disconnect(); - return( -1 ); - } - image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); - break; - } - default : - { - Error( "Unexpected image format encountered" ); - Disconnect(); - return( -1 ); - } - } - return( 0 ); + return( 0 ); } int RemoteCameraHttp::PostCapture() { - return( 0 ); + return( 0 ); } diff --git a/src/zm_remote_camera_http.h b/src/zm_remote_camera_http.h index bfadecce5..a5994b9b3 100644 --- a/src/zm_remote_camera_http.h +++ b/src/zm_remote_camera_http.h @@ -33,31 +33,32 @@ class RemoteCameraHttp : public RemoteCamera { protected: - std::string request; - struct timeval timeout; - //struct hostent *hp; - //struct sockaddr_in sa; - int sd; - Buffer buffer; - enum { SINGLE_IMAGE, MULTI_IMAGE } mode; - enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; - enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; - enum { SIMPLE, REGEXP } method; + std::string request; + struct timeval timeout; + //struct hostent *hp; + //struct sockaddr_in sa; + int sd; + Buffer buffer; + enum { SINGLE_IMAGE, MULTI_IMAGE } mode; + enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; + enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; + enum { SIMPLE, REGEXP } method; public: - RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~RemoteCameraHttp(); + RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~RemoteCameraHttp(); - void Initialise(); - void Terminate() { Disconnect(); } - int Connect(); - int Disconnect(); - int SendRequest(); - int ReadData( Buffer &buffer, int bytes_expected=0 ); - int GetResponse(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + void Initialise(); + void Terminate() { Disconnect(); } + int Connect(); + int Disconnect(); + int SendRequest(); + int ReadData( Buffer &buffer, int bytes_expected=0 ); + int GetResponse(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);}; }; #endif // ZM_REMOTE_CAMERA_HTTP_H diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 50c8ed3dc..5256b1de2 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -28,344 +28,566 @@ #include #include -RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ), - rtsp_describe( p_rtsp_describe ), - rtspThread( 0 ) +RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : + RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), + rtsp_describe( p_rtsp_describe ), + rtspThread( 0 ) { - if ( p_method == "rtpUni" ) - method = RtspThread::RTP_UNICAST; - else if ( p_method == "rtpMulti" ) - method = RtspThread::RTP_MULTICAST; - else if ( p_method == "rtpRtsp" ) - method = RtspThread::RTP_RTSP; - else if ( p_method == "rtpRtspHttp" ) - method = RtspThread::RTP_RTSP_HTTP; - else - Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id ); + if ( p_method == "rtpUni" ) + method = RtspThread::RTP_UNICAST; + else if ( p_method == "rtpMulti" ) + method = RtspThread::RTP_MULTICAST; + else if ( p_method == "rtpRtsp" ) + method = RtspThread::RTP_RTSP; + else if ( p_method == "rtpRtspHttp" ) + method = RtspThread::RTP_RTSP_HTTP; + else + Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id ); - if ( capture ) - { - Initialise(); - } - - mFormatContext = NULL; - mVideoStreamId = -1; - mCodecContext = NULL; - mCodec = NULL; - mRawFrame = NULL; - mFrame = NULL; - frameCount = 0; - -#if HAVE_LIBSWSCALE - mConvertContext = NULL; + if ( capture ) + { + Initialise(); + } + + mFormatContext = NULL; + mVideoStreamId = -1; + mAudioStreamId = -1; + mCodecContext = NULL; + mCodec = NULL; + mRawFrame = NULL; + mFrame = NULL; + frameCount = 0; + wasRecording = false; + startTime=0; + +#if HAVE_LIBSWSCALE + mConvertContext = NULL; #endif - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = AV_PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + } RemoteCameraRtsp::~RemoteCameraRtsp() { #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - av_frame_free( &mFrame ); - av_frame_free( &mRawFrame ); + av_frame_free( &mFrame ); + av_frame_free( &mRawFrame ); #else - av_freep( &mFrame ); - av_freep( &mRawFrame ); + av_freep( &mFrame ); + av_freep( &mRawFrame ); #endif - + #if HAVE_LIBSWSCALE - if ( mConvertContext ) - { - sws_freeContext( mConvertContext ); - mConvertContext = NULL; - } + if ( mConvertContext ) + { + sws_freeContext( mConvertContext ); + mConvertContext = NULL; + } #endif - if ( mCodecContext ) - { - avcodec_close( mCodecContext ); - mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class - } + if ( mCodecContext ) + { + avcodec_close( mCodecContext ); + mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class + } - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void RemoteCameraRtsp::Initialise() { - RemoteCamera::Initialise(); + RemoteCamera::Initialise(); - int max_size = width*height*colours; + int max_size = width*height*colours; - buffer.size( max_size ); + // This allocates a buffer able to hold a raw fframe, which is a little artbitrary. Might be nice to get some + // decent data on how large a buffer is really needed. I think in ffmpeg there are now some functions to do that. + buffer.size( max_size ); - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); - av_register_all(); + av_register_all(); - Connect(); + Connect(); } void RemoteCameraRtsp::Terminate() { - Disconnect(); + Disconnect(); } int RemoteCameraRtsp::Connect() { - rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe ); + rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe ); - rtspThread->start(); + rtspThread->start(); - return( 0 ); + return( 0 ); } int RemoteCameraRtsp::Disconnect() { - if ( rtspThread ) - { - rtspThread->stop(); - rtspThread->join(); - delete rtspThread; - rtspThread = 0; - } - return( 0 ); + if ( rtspThread ) + { + rtspThread->stop(); + rtspThread->join(); + delete rtspThread; + rtspThread = 0; + } + return( 0 ); } int RemoteCameraRtsp::PrimeCapture() { - Debug( 2, "Waiting for sources" ); - for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) - { - usleep( 100000 ); - } - if ( !rtspThread->hasSources() ) - Fatal( "No RTSP sources" ); + Debug( 2, "Waiting for sources" ); + for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) + { + usleep( 100000 ); + } + if ( !rtspThread->hasSources() ) + Fatal( "No RTSP sources" ); - Debug( 2, "Got sources" ); + Debug( 2, "Got sources" ); - mFormatContext = rtspThread->getFormatContext(); + mFormatContext = rtspThread->getFormatContext(); - // Find first video stream present - mVideoStreamId = -1; - - for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) + // Find first video stream present + mVideoStreamId = -1; + mAudioStreamId = -1; + + // Find the first video stream. + for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) #else - if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) #endif - { - mVideoStreamId = i; - break; + { + if ( mVideoStreamId == -1 ) { + mVideoStreamId = i; + continue; + } else { + Debug(2, "Have another video stream." ); + } + } +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) +#else + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO ) +#endif + { + if ( mAudioStreamId == -1 ) { + mAudioStreamId = i; + } else { + Debug(2, "Have another audio stream." ); + } + } + } - if ( mVideoStreamId == -1 ) - Fatal( "Unable to locate video stream" ); + if ( mVideoStreamId == -1 ) + Fatal( "Unable to locate video stream" ); + if ( mAudioStreamId == -1 ) + Debug( 3, "Unable to locate audio stream" ); - // Get a pointer to the codec context for the video stream - mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; + // Get a pointer to the codec context for the video stream + mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; - // Find the decoder for the video stream - mCodec = avcodec_find_decoder( mCodecContext->codec_id ); - if ( mCodec == NULL ) - Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id ); + // Find the decoder for the video stream + mCodec = avcodec_find_decoder( mCodecContext->codec_id ); + if ( mCodec == NULL ) + Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id ); - // Open codec + // Open codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - if ( avcodec_open( mCodecContext, mCodec ) < 0 ) + if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else - if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) + if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif - Panic( "Can't open codec" ); + Panic( "Can't open codec" ); - // Allocate space for the native video frame + // Allocate space for the native video frame #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mRawFrame = av_frame_alloc(); + mRawFrame = av_frame_alloc(); #else - mRawFrame = avcodec_alloc_frame(); + mRawFrame = avcodec_alloc_frame(); #endif - // Allocate space for the converted video frame + // Allocate space for the converted video frame #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - mFrame = av_frame_alloc(); + mFrame = av_frame_alloc(); #else - mFrame = avcodec_alloc_frame(); + mFrame = avcodec_alloc_frame(); #endif - if(mRawFrame == NULL || mFrame == NULL) - Fatal( "Unable to allocate frame(s)"); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } -/* + if(mRawFrame == NULL || mFrame == NULL) + Fatal( "Unable to allocate frame(s)"); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height, 1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } +/* #if HAVE_LIBSWSCALE - if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { - Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); - } + if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { + Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); + } - if(!sws_isSupportedOutput(imagePixFormat)) { - Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - } - + if(!sws_isSupportedOutput(imagePixFormat)) { + Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + } + #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); #endif // HAVE_LIBSWSCALE */ - return( 0 ); + return( 0 ); } -int RemoteCameraRtsp::PreCapture() -{ - if ( !rtspThread->isRunning() ) - return( -1 ); - if ( !rtspThread->hasSources() ) - { - Error( "Cannot precapture, no RTP sources" ); - return( -1 ); - } - return( 0 ); +int RemoteCameraRtsp::PreCapture() { + if ( !rtspThread->isRunning() ) + return( -1 ); + if ( !rtspThread->hasSources() ) + { + Error( "Cannot precapture, no RTP sources" ); + return( -1 ); + } + return( 0 ); } -int RemoteCameraRtsp::Capture( Image &image ) -{ - AVPacket packet; - uint8_t* directbuffer; - int frameComplete = false; - - /* 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."); - return (-1); - } - - while ( true ) - { - buffer.clear(); - if ( !rtspThread->isRunning() ) - return (-1); +int RemoteCameraRtsp::Capture( Image &image ) { + AVPacket packet; + uint8_t* directbuffer; + int frameComplete = false; + + /* 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."); + return (-1); + } + + while ( true ) { + buffer.clear(); + if ( !rtspThread->isRunning() ) + return (-1); - if ( rtspThread->getFrame( buffer ) ) - { - Debug( 3, "Read frame %d bytes", buffer.size() ); - Debug( 4, "Address %p", buffer.head() ); - Hexdump( 4, buffer.head(), 16 ); + if ( rtspThread->getFrame( buffer ) ) { + Debug( 3, "Read frame %d bytes", buffer.size() ); + Debug( 4, "Address %p", buffer.head() ); + Hexdump( 4, buffer.head(), 16 ); - if ( !buffer.size() ) - return( -1 ); + if ( !buffer.size() ) + return( -1 ); - if(mCodecContext->codec_id == AV_CODEC_ID_H264) - { - // SPS and PPS frames should be saved and appended to IDR frames - int nalType = (buffer.head()[3] & 0x1f); - - // SPS - if(nalType == 7) - { - lastSps = buffer; - continue; - } - // PPS - else if(nalType == 8) - { - lastPps = buffer; - continue; - } - // IDR - else if(nalType == 5) - { - buffer += lastSps; - buffer += lastPps; - } - } + if(mCodecContext->codec_id == AV_CODEC_ID_H264) { + // SPS and PPS frames should be saved and appended to IDR frames + int nalType = (buffer.head()[3] & 0x1f); + + // SPS The SPS NAL unit contains parameters that apply to a series of consecutive coded video pictures + if(nalType == 7) + { + lastSps = buffer; + continue; + } + // PPS The PPS NAL unit contains parameters that apply to the decoding of one or more individual pictures inside a coded video sequence + else if(nalType == 8) + { + lastPps = buffer; + continue; + } + // IDR + else if(nalType == 5) + { + buffer += lastSps; + buffer += lastPps; + } + } else { + Debug(3, "Not an h264 packet"); + } - av_init_packet( &packet ); - - while ( !frameComplete && buffer.size() > 0 ) - { - packet.data = buffer.head(); - packet.size = buffer.size(); + av_init_packet( &packet ); + + while ( !frameComplete && buffer.size() > 0 ) { + packet.data = buffer.head(); + packet.size = buffer.size(); + + // So I think this is the magic decode step. Result is a raw image? + #if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) + int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); + #else + int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); + #endif + if ( len < 0 ) { + Error( "Error while decoding frame %d", frameCount ); + Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); + buffer.clear(); + continue; + } + Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); + //if ( buffer.size() < 400 ) + //Hexdump( 0, buffer.head(), buffer.size() ); + + buffer -= len; + } + // At this point, we either have a frame or ran out of buffer. What happens if we run out of buffer? + if ( frameComplete ) { + + Debug( 3, "Got frame %d", frameCount ); + + avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height ); + + #if HAVE_LIBSWSCALE + if(mConvertContext == NULL) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if(mConvertContext == NULL) + Fatal( "Unable to create conversion context"); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); + #else // HAVE_LIBSWSCALE + Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); + #endif // HAVE_LIBSWSCALE + + frameCount++; + + } /* frame complete */ + + #if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( &packet ); + #else + av_free_packet( &packet ); + #endif + } /* getFrame() */ + + if(frameComplete) + return (0); + + } // end while true + + // can never get here. + return (0) ; +} + +//int RemoteCameraRtsp::ReadData(void *opaque, uint8_t *buf, int bufSize) { + + //if ( buffer.size() > bufSize ) { + //buf = buffer.head(); + //buffer -= bufSize; + //} else { + //Error("Implement me"); + //return -1; + //} +//} + +//Function to handle capture and store +int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* event_file ) { + AVPacket packet; + uint8_t* directbuffer; + int frameComplete = false; + + /* 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."); + return (-1); + } + + while ( true ) { + + buffer.clear(); + if ( !rtspThread->isRunning() ) + return (-1); + + if ( rtspThread->getFrame( buffer ) ) { + Debug( 3, "Read frame %d bytes", buffer.size() ); + Debug( 4, "Address %p", buffer.head() ); + Hexdump( 4, buffer.head(), 16 ); + + if ( !buffer.size() ) + return( -1 ); + + if(mCodecContext->codec_id == AV_CODEC_ID_H264) { + // SPS and PPS frames should be saved and appended to IDR frames + int nalType = (buffer.head()[3] & 0x1f); + + // SPS + if(nalType == 7) { + lastSps = buffer; + continue; + } + // PPS + else if(nalType == 8) { + lastPps = buffer; + continue; + } + // IDR + else if(nalType == 5) { + buffer += lastSps; + buffer += lastPps; + } + } // end if H264, what about other codecs? + + av_init_packet( &packet ); + + // Why are we checking for it being the video stream? Because it might be audio or something else. + // Um... we just initialized packet... we can't be testing for what it is yet.... + if ( packet.stream_index == mVideoStreamId ) { + + while ( !frameComplete && buffer.size() > 0 ) { + packet.data = buffer.head(); + packet.size = buffer.size(); + + // So this does the decode #if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) - int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); + int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); #else - int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); + int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); #endif - if ( len < 0 ) - { - Error( "Error while decoding frame %d", frameCount ); - Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); - buffer.clear(); - continue; - } - Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); - //if ( buffer.size() < 400 ) - //Hexdump( 0, buffer.head(), buffer.size() ); - - buffer -= len; + if ( len < 0 ) { + Error( "Error while decoding frame %d", frameCount ); + Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); + buffer.clear(); + continue; + } + Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); + //if ( buffer.size() < 400 ) + //Hexdump( 0, buffer.head(), buffer.size() ); + + buffer -= len; + } // end while get & decode a frame + + if ( frameComplete ) { + + Debug( 3, "Got frame %d", frameCount ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(mFrame->data, mFrame->linesize, + directbuffer, imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)mFrame, directbuffer, + imagePixFormat, width, height); +#endif + + //Video recording + if ( recording && !wasRecording ) { + //Instantiate the video storage module + + videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime); + wasRecording = true; + strcpy(oldDirectory, event_file); + + } else if ( !recording && wasRecording && videoStore ) { + // Why are we deleting the videostore? Becase for soem reason we are no longer recording? How does that happen? + Info("Deleting videoStore instance"); + delete videoStore; + videoStore = NULL; + } + + //The directory we are recording to is no longer tied to the current event. Need to re-init the videostore with the correct directory and start recording again + if ( recording && wasRecording && (strcmp(oldDirectory, event_file)!=0) && (packet.flags & AV_PKT_FLAG_KEY) ) { + //don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?...if we store our key frame location with the event will that be enough? + Info("Re-starting video storage module"); + if ( videoStore ) { + delete videoStore; + videoStore = NULL; + } + + videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime); + strcpy( oldDirectory, event_file ); + } + + if ( videoStore && recording ) { + //Write the packet to our video store + int ret = videoStore->writeVideoFramePacket(&packet, mFormatContext->streams[mVideoStreamId]);//, &lastKeyframePkt); + if ( ret < 0 ) {//Less than zero and we skipped a frame + av_free_packet( &packet ); + return 0; + } + } - } - if ( frameComplete ) { - - Debug( 3, "Got frame %d", frameCount ); - - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); - #if HAVE_LIBSWSCALE - if(mConvertContext == NULL) { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); +// Why are we re-scaling after writing out the packet? + if(mConvertContext == NULL) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); - if(mConvertContext == NULL) - Fatal( "Unable to create conversion context"); - } - - if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) - Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); + if(mConvertContext == NULL) + Fatal( "Unable to create conversion context"); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); #endif // HAVE_LIBSWSCALE - - frameCount++; - } /* frame complete */ - + frameCount++; + + } /* frame complete */ + } else if ( packet.stream_index == mAudioStreamId ) { + Debug( 4, "Got audio packet" ); + if ( videoStore && recording ) { + if ( record_audio ) { + Debug( 4, "Storing Audio packet" ); + //Write the packet to our video store + int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame + if ( ret < 0 ) { //Less than zero and we skipped a frame #if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) - av_packet_unref( &packet); + av_packet_unref( &packet ); #else - av_free_packet( &packet ); + av_free_packet( &packet ); #endif - } /* getFrame() */ - - if(frameComplete) - return (0); - - } - return (0) ; -} + return 0; + } + } else { + Debug( 4, "Not storing audio" ); + } + } + } // end if video or audio packet + +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( &packet ); +#else + av_free_packet( &packet ); +#endif + } /* getFrame() */ + + if(frameComplete) + return (0); + } // end while true + + // can never get here. + return (0) ; +} // int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* event_file ) int RemoteCameraRtsp::PostCapture() { - return( 0 ); + return( 0 ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index 20261f82d..6de7fddc1 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -26,6 +26,7 @@ #include "zm_utils.h" #include "zm_rtsp.h" #include "zm_ffmpeg.h" +#include "zm_videostore.h" // // Class representing 'rtsp' cameras, i.e. those which are @@ -35,51 +36,56 @@ class RemoteCameraRtsp : public RemoteCamera { protected: - struct sockaddr_in rtsp_sa; - struct sockaddr_in rtcp_sa; - int rtsp_sd; - int rtp_sd; - int rtcp_sd; - bool rtsp_describe; + struct sockaddr_in rtsp_sa; + struct sockaddr_in rtcp_sa; + int rtsp_sd; + int rtp_sd; + int rtcp_sd; + bool rtsp_describe; - Buffer buffer; - Buffer lastSps; - Buffer lastPps; + Buffer buffer; + Buffer lastSps; + Buffer lastPps; - RtspThread::RtspMethod method; + RtspThread::RtspMethod method; - RtspThread *rtspThread; + RtspThread *rtspThread; - int frameCount; - + int frameCount; + #if HAVE_LIBAVFORMAT - AVFormatContext *mFormatContext; - int mVideoStreamId; - AVCodecContext *mCodecContext; - AVCodec *mCodec; - AVFrame *mRawFrame; - AVFrame *mFrame; - _AVPIXELFORMAT imagePixFormat; + AVFormatContext *mFormatContext; + int mVideoStreamId; + int mAudioStreamId; + AVCodecContext *mCodecContext; + AVCodec *mCodec; + AVFrame *mRawFrame; + AVFrame *mFrame; + _AVPIXELFORMAT imagePixFormat; #endif // HAVE_LIBAVFORMAT + bool wasRecording; + VideoStore *videoStore; + char oldDirectory[4096]; + int64_t startTime; #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext; + struct SwsContext *mConvertContext; #endif public: - RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~RemoteCameraRtsp(); + RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + ~RemoteCameraRtsp(); - void Initialise(); - void Terminate(); - int Connect(); - int Disconnect(); - - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + void Initialise(); + void Terminate(); + int Connect(); + int Disconnect(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); + int CaptureAndRecord( Image &image, bool recording, char* event_directory ); }; #endif // ZM_REMOTE_CAMERA_RTSP_H diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp index bbdd59a6a..16bb171c4 100644 --- a/src/zm_rtp_source.cpp +++ b/src/zm_rtp_source.cpp @@ -27,358 +27,374 @@ #if HAVE_LIBAVCODEC RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : - mId( id ), - mSsrc( ssrc ), - mLocalHost( localHost ), - mRemoteHost( remoteHost ), - mRtpClock( rtpClock ), - mCodecId( codecId ), - mFrame( 65536 ), - mFrameCount( 0 ), - mFrameGood( true ), - mFrameReady( false ), - mFrameProcessed( false ) + mId( id ), + mSsrc( ssrc ), + mLocalHost( localHost ), + mRemoteHost( remoteHost ), + mRtpClock( rtpClock ), + mCodecId( codecId ), + mFrame( 65536 ), + mFrameCount( 0 ), + mFrameGood( true ), + mFrameReady( false ), + mFrameProcessed( false ) { - char hostname[256] = ""; - gethostname( hostname, sizeof(hostname) ); + char hostname[256] = ""; + gethostname( hostname, sizeof(hostname) ); - mCname = stringtf( "zm-%d@%s", mId, hostname ); - Debug( 3, "RTP CName = %s", mCname.c_str() ); + mCname = stringtf( "zm-%d@%s", mId, hostname ); + Debug( 3, "RTP CName = %s", mCname.c_str() ); - init( seq ); - mMaxSeq = seq - 1; - mProbation = MIN_SEQUENTIAL; + init( seq ); + mMaxSeq = seq - 1; + mProbation = MIN_SEQUENTIAL; - mLocalPortChans[0] = localPortBase; - mLocalPortChans[1] = localPortBase+1; + mLocalPortChans[0] = localPortBase; + mLocalPortChans[1] = localPortBase+1; - mRemotePortChans[0] = remotePortBase; - mRemotePortChans[1] = remotePortBase+1; + mRemotePortChans[0] = remotePortBase; + mRemotePortChans[1] = remotePortBase+1; - mRtpFactor = mRtpClock; + mRtpFactor = mRtpClock; - mBaseTimeReal = tvNow(); - mBaseTimeNtp = tvZero(); - mBaseTimeRtp = rtpTime; + mBaseTimeReal = tvNow(); + mBaseTimeNtp = tvZero(); + mBaseTimeRtp = rtpTime; - mLastSrTimeReal = tvZero(); - mLastSrTimeNtp = tvZero(); - mLastSrTimeRtp = 0; - - if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) - Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); + mLastSrTimeReal = tvZero(); + mLastSrTimeNtp = tvZero(); + mLastSrTimeRtp = 0; + + if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) + Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); } void RtpSource::init( uint16_t seq ) { - Debug( 3, "Initialising sequence" ); - mBaseSeq = seq; - mMaxSeq = seq; - mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false - mCycles = 0; - mReceivedPackets = 0; - mReceivedPrior = 0; - mExpectedPrior = 0; - // other initialization - mJitter = 0; - mTransit = 0; + Debug( 3, "Initialising sequence" ); + mBaseSeq = seq; + mMaxSeq = seq; + mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false + mCycles = 0; + mReceivedPackets = 0; + mReceivedPrior = 0; + mExpectedPrior = 0; + // other initialization + mJitter = 0; + mTransit = 0; } bool RtpSource::updateSeq( uint16_t seq ) { - uint16_t uDelta = seq - mMaxSeq; + uint16_t uDelta = seq - mMaxSeq; - // Source is not valid until MIN_SEQUENTIAL packets with - // sequential sequence numbers have been received. - Debug( 5, "Seq: %d", seq ); + // Source is not valid until MIN_SEQUENTIAL packets with + // sequential sequence numbers have been received. + Debug( 5, "Seq: %d", seq ); - if ( mProbation) - { - // packet is in sequence - if ( seq == mMaxSeq + 1) + if ( mProbation) { - Debug( 3, "Sequence in probation %d, in sequence", mProbation ); - mProbation--; - mMaxSeq = seq; - if ( mProbation == 0 ) - { - init( seq ); - mReceivedPackets++; + // packet is in sequence + if ( seq == mMaxSeq + 1) + { + Debug( 3, "Sequence in probation %d, in sequence", mProbation ); + mProbation--; + mMaxSeq = seq; + if ( mProbation == 0 ) + { + init( seq ); + mReceivedPackets++; + return( true ); + } + } + else + { + Warning( "Sequence in probation %d, out of sequence", mProbation ); + mProbation = MIN_SEQUENTIAL - 1; + mMaxSeq = seq; + return( false ); + } return( true ); - } } - else + else if ( uDelta < MAX_DROPOUT ) { - Warning( "Sequence in probation %d, out of sequence", mProbation ); - mProbation = MIN_SEQUENTIAL - 1; - mMaxSeq = seq; - return( false ); - } - return( true ); - } - else if ( uDelta < MAX_DROPOUT ) - { - if ( uDelta == 1 ) - { - Debug( 3, "Packet in sequence, gap %d", uDelta ); - } - else - { - Warning( "Packet in sequence, gap %d", uDelta ); - } + if ( uDelta == 1 ) + { + Debug( 4, "Packet in sequence, gap %d", uDelta ); + } + else + { + Warning( "Packet in sequence, gap %d", uDelta ); + } - // in order, with permissible gap - if ( seq < mMaxSeq ) - { - // Sequence number wrapped - count another 64K cycle. - mCycles += RTP_SEQ_MOD; + // in order, with permissible gap + if ( seq < mMaxSeq ) + { + // Sequence number wrapped - count another 64K cycle. + mCycles += RTP_SEQ_MOD; + } + mMaxSeq = seq; } - mMaxSeq = seq; - } - else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) - { - Warning( "Packet out of sequence, gap %d", uDelta ); - // the sequence number made a very large jump - if ( seq == mBadSeq ) + else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) { - Debug( 3, "Restarting sequence" ); - // Two sequential packets -- assume that the other side - // restarted without telling us so just re-sync - // (i.e., pretend this was the first packet). - init( seq ); + Warning( "Packet out of sequence, gap %d", uDelta ); + // the sequence number made a very large jump + if ( seq == mBadSeq ) + { + Debug( 3, "Restarting sequence" ); + // Two sequential packets -- assume that the other side + // restarted without telling us so just re-sync + // (i.e., pretend this was the first packet). + init( seq ); + } + else + { + mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); + return( false ); + } } else { - mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); - return( false ); + Warning( "Packet duplicate or reordered, gap %d", uDelta ); + // duplicate or reordered packet + return( false ); } - } - else - { - Warning( "Packet duplicate or reordered, gap %d", uDelta ); - // duplicate or reordered packet - return( false ); - } - mReceivedPackets++; - return( uDelta==1?true:false ); + mReceivedPackets++; + return( uDelta==1?true:false ); } void RtpSource::updateJitter( const RtpDataHeader *header ) { - if ( mRtpFactor > 0 ) - { - Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); - uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); - Debug( 5, "Local RTP time = %x", localTimeRtp ); - Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); - uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); - Debug( 5, "Packet transit RTP time = %x", packetTransit ); - - if ( mTransit > 0 ) + if ( mRtpFactor > 0 ) { - // Jitter - int d = packetTransit - mTransit; - Debug( 5, "Jitter D = %d", d ); - if ( d < 0 ) - d = -d; - //mJitter += (1./16.) * ((double)d - mJitter); - mJitter += d - ((mJitter + 8) >> 4); + Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); + uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); + Debug( 5, "Local RTP time = %x", localTimeRtp ); + Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); + uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); + Debug( 5, "Packet transit RTP time = %x", packetTransit ); + + if ( mTransit > 0 ) + { + // Jitter + int d = packetTransit - mTransit; + Debug( 5, "Jitter D = %d", d ); + if ( d < 0 ) + d = -d; + //mJitter += (1./16.) * ((double)d - mJitter); + mJitter += d - ((mJitter + 8) >> 4); + } + mTransit = packetTransit; } - mTransit = packetTransit; - } - else - { - mJitter = 0; - } - Debug( 5, "RTP Jitter: %d", mJitter ); + else + { + mJitter = 0; + } + Debug( 5, "RTP Jitter: %d", mJitter ); } void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) { - struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); + struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); - Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - - if ( mBaseTimeNtp.tv_sec == 0 ) - { - mBaseTimeReal = tvNow(); - mBaseTimeNtp = ntpTime; - mBaseTimeRtp = rtpTime; - } - else if ( !mRtpClock ) - { - Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); + + if ( mBaseTimeNtp.tv_sec == 0 ) + { + mBaseTimeReal = tvNow(); + mBaseTimeNtp = ntpTime; + mBaseTimeRtp = rtpTime; + } + else if ( !mRtpClock ) + { + Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); + Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); - uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; + double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); + uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; - //Debug( 5, "Real-diff: %.6f", diffRealTime ); - Debug( 5, "NTP-diff: %.6f", diffNtpTime ); - Debug( 5, "RTP-diff: %d", diffRtpTime ); + //Debug( 5, "Real-diff: %.6f", diffRealTime ); + Debug( 5, "NTP-diff: %.6f", diffNtpTime ); + Debug( 5, "RTP-diff: %d", diffRtpTime ); - mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); + mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); - Debug( 5, "RTPfactor: %d", mRtpFactor ); - } - mLastSrTimeNtpSecs = ntpTimeSecs; - mLastSrTimeNtpFrac = ntpTimeFrac; - mLastSrTimeNtp = ntpTime; - mLastSrTimeRtp = rtpTime; + Debug( 5, "RTPfactor: %d", mRtpFactor ); + } + mLastSrTimeNtpSecs = ntpTimeSecs; + mLastSrTimeNtpFrac = ntpTimeFrac; + mLastSrTimeNtp = ntpTime; + mLastSrTimeRtp = rtpTime; } void RtpSource::updateRtcpStats() { - uint32_t extendedMax = mCycles + mMaxSeq; - mExpectedPackets = extendedMax - mBaseSeq + 1; + uint32_t extendedMax = mCycles + mMaxSeq; + mExpectedPackets = extendedMax - mBaseSeq + 1; - Debug( 5, "Expected packets = %d", mExpectedPackets ); + Debug( 5, "Expected packets = %d", mExpectedPackets ); - // The number of packets lost is defined to be the number of packets - // expected less the number of packets actually received: - mLostPackets = mExpectedPackets - mReceivedPackets; - Debug( 5, "Lost packets = %d", mLostPackets ); + // The number of packets lost is defined to be the number of packets + // expected less the number of packets actually received: + mLostPackets = mExpectedPackets - mReceivedPackets; + Debug( 5, "Lost packets = %d", mLostPackets ); - uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; - Debug( 5, "Expected interval = %d", expectedInterval ); - mExpectedPrior = mExpectedPackets; - uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; - Debug( 5, "Received interval = %d", receivedInterval ); - mReceivedPrior = mReceivedPackets; - uint32_t lostInterval = expectedInterval - receivedInterval; - Debug( 5, "Lost interval = %d", lostInterval ); + uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; + Debug( 5, "Expected interval = %d", expectedInterval ); + mExpectedPrior = mExpectedPackets; + uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; + Debug( 5, "Received interval = %d", receivedInterval ); + mReceivedPrior = mReceivedPackets; + uint32_t lostInterval = expectedInterval - receivedInterval; + Debug( 5, "Lost interval = %d", lostInterval ); - if ( expectedInterval == 0 || lostInterval <= 0 ) - mLostFraction = 0; - else - mLostFraction = (lostInterval << 8) / expectedInterval; - Debug( 5, "Lost fraction = %d", mLostFraction ); + if ( expectedInterval == 0 || lostInterval <= 0 ) + mLostFraction = 0; + else + mLostFraction = (lostInterval << 8) / expectedInterval; + Debug( 5, "Lost fraction = %d", mLostFraction ); } bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) { - const RtpDataHeader *rtpHeader; - rtpHeader = (RtpDataHeader *)packet; - int rtpHeaderSize = 12 + rtpHeader->cc * 4; - // No need to check for nal type as non fragmented packets already have 001 start sequence appended - bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); - bool thisM = rtpHeader->m || h264FragmentEnd; + const RtpDataHeader *rtpHeader; + rtpHeader = (RtpDataHeader *)packet; + int rtpHeaderSize = 12 + rtpHeader->cc * 4; + // No need to check for nal type as non fragmented packets already have 001 start sequence appended + bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); + // M stands for Marker, it is the 8th bit + // The interpretation of the marker is defined by a profile. It is intended + // to allow significant events such as frame boundaries to be marked in the + // packet stream. A profile may define additional marker bits or specify + // that there is no marker bit by changing the number of bits in the payload type field. + bool thisM = rtpHeader->m || h264FragmentEnd; - if ( updateSeq( ntohs(rtpHeader->seqN) ) ) - { - Hexdump( 4, packet+rtpHeaderSize, 16 ); - - if ( mFrameGood ) + if ( updateSeq( ntohs(rtpHeader->seqN) ) ) { - int extraHeader = 0; - - if( mCodecId == AV_CODEC_ID_H264 ) - { - int nalType = (packet[rtpHeaderSize] & 0x1f); - - switch (nalType) + Hexdump( 4, packet+rtpHeaderSize, 16 ); + + if ( mFrameGood ) { - case 24: - { - extraHeader = 2; - break; - } - case 25: case 26: case 27: - { - extraHeader = 3; - break; - } - // FU-A and FU-B - case 28: case 29: - { - // Is this NAL the first NAL in fragmentation sequence - if ( packet[rtpHeaderSize+1] & 0x80 ) + int extraHeader = 0; + + if( mCodecId == AV_CODEC_ID_H264 ) { - // Now we will form new header of frame - mFrame.append( "\x0\x0\x1\x0", 4 ); - // Reconstruct NAL header from FU headers - *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) | - (packet[rtpHeaderSize] & 0xe0); + int nalType = (packet[rtpHeaderSize] & 0x1f); + Debug( 3, "Have H264 frame: nal type is %d", nalType ); + + switch (nalType) + { + case 24: // STAP-A + { + extraHeader = 2; + break; + } + case 25: // STAP-B + case 26: // MTAP-16 + case 27: // MTAP-24 + { + extraHeader = 3; + break; + } + // FU-A and FU-B + case 28: case 29: + { + // Is this NAL the first NAL in fragmentation sequence + if ( packet[rtpHeaderSize+1] & 0x80 ) + { + // Now we will form new header of frame + mFrame.append( "\x0\x0\x1\x0", 4 ); + // Reconstruct NAL header from FU headers + *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) | + (packet[rtpHeaderSize] & 0xe0); + } + + extraHeader = 2; + break; + } + default: { + Debug(3, "Unhandled nalType %d", nalType ); + } + } + + // Append NAL frame start code + if ( !mFrame.size() ) + mFrame.append( "\x0\x0\x1", 3 ); } - - extraHeader = 2; - break; - } + mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); + } else { + Debug( 3, "NOT H264 frame: type is %d", mCodecId ); } - - // Append NAL frame start code - if ( !mFrame.size() ) - mFrame.append( "\x0\x0\x1", 3 ); - } - mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); - } - Hexdump( 4, mFrame.head(), 16 ); + Hexdump( 4, mFrame.head(), 16 ); - if ( thisM ) - { - if ( mFrameGood ) - { - Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); - - mFrameProcessed.setValueImmediate( false ); - mFrameReady.updateValueSignal( true ); - if ( !mFrameProcessed.getValueImmediate() ) + if ( thisM ) { - for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ ) - if( count > 1 ) - return( false ); + if ( mFrameGood ) + { + Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); + + mFrameProcessed.setValueImmediate( false ); + mFrameReady.updateValueSignal( true ); + if ( !mFrameProcessed.getValueImmediate() ) + { + // What is the point of this for loop? Is it just me, or will it call getUpdatedValue once or twice? Could it not be better written as + // if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false; + + for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ ) + if( count > 1 ) + return( false ); + } + mFrameCount++; + } + else + { + Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); + } + mFrame.clear(); } - mFrameCount++; - } - else - { - Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); - } - mFrame.clear(); - } - } - else - { - if ( mFrame.size() ) - { - Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); } else { - Warning( "Discarding frame %d", mFrameCount ); + if ( mFrame.size() ) + { + Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); + } + else + { + Warning( "Discarding frame %d", mFrameCount ); + } + mFrameGood = false; + mFrame.clear(); } - mFrameGood = false; - mFrame.clear(); - } - if ( thisM ) - { - mFrameGood = true; - prevM = true; - } - else - prevM = false; + if ( thisM ) + { + mFrameGood = true; + prevM = true; + } + else + prevM = false; - updateJitter( rtpHeader ); + updateJitter( rtpHeader ); - return( true ); + return( true ); } bool RtpSource::getFrame( Buffer &buffer ) { - Debug( 3, "Getting frame" ); - if ( !mFrameReady.getValueImmediate() ) - { - // Allow for a couple of spurious returns - for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) - if ( count > 1 ) - return( false ); - } - buffer = mFrame; - mFrameReady.setValueImmediate( false ); - mFrameProcessed.updateValueSignal( true ); - Debug( 3, "Copied %d bytes", buffer.size() ); - return( true ); + Debug( 3, "Getting frame" ); + if ( !mFrameReady.getValueImmediate() ) + { + // Allow for a couple of spurious returns + for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) + if ( count > 1 ) + return( false ); + } + buffer = mFrame; + mFrameReady.setValueImmediate( false ); + mFrameProcessed.updateValueSignal( true ); + Debug( 4, "Copied %d bytes", buffer.size() ); + return( true ); } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_storage.cpp b/src/zm_storage.cpp index 14a6cfc01..ff2bce5a9 100644 --- a/src/zm_storage.cpp +++ b/src/zm_storage.cpp @@ -50,8 +50,8 @@ Storage::Storage( unsigned int p_id ) { char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "SELECT Id, Name, Path from Storage WHERE Id=%d", p_id ); Debug(1,"Loading Storage for %d using %s", p_id, sql ); - MYSQL_ROW dbrow = zmDbFetchOne( sql ); - if ( ! dbrow ) { + zmDbRow dbrow; + if ( ! dbrow.fetch( sql ) ) { Error( "Unable to load storage area for id %d: %s", p_id, mysql_error( &dbconn ) ); } else { unsigned int index = 0; diff --git a/src/zm_video.cpp b/src/zm_video.cpp new file mode 100644 index 000000000..82cb75d1d --- /dev/null +++ b/src/zm_video.cpp @@ -0,0 +1,518 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +#include "zm.h" +#include "zm_video.h" +#include "zm_image.h" +#include "zm_utils.h" +#include "zm_rgb.h" +#include + +VideoWriter::VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) : +container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_height), colours(p_colours), subpixelorder(p_subpixelorder), frame_count(0) { + Debug(7,"Video object created"); + + /* Parameter checking */ + if(path.empty()) { + Error("Invalid file path"); + } + if(!width || !height) { + Error("Invalid width or height"); + } + +} + +VideoWriter::~VideoWriter() { + Debug(7,"Video object destroyed"); + +} + +int VideoWriter::Reset(const char* new_path) { + /* Common variables reset */ + + /* If there is a new path, use it */ + if(new_path != NULL) { + path = new_path; + } + + /* Reset frame counter */ + frame_count = 0; + + return 0; +} + + +#if ZM_HAVE_VIDEOWRITER_X264MP4 +X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector* p_user_params) : VideoWriter("mp4", "h264", p_path, p_width, p_height, p_colours, p_subpixelorder), bOpen(false), bGotH264AVCInfo(false), bFirstFrame(true) { + + /* Initialize ffmpeg if it hasn't been initialized yet */ + FFMPEGInit(); + + /* Initialize swscale */ + zm_pf = GetFFMPEGPixelFormat(colours,subpixelorder); + if(zm_pf == 0) { + Error("Unable to match ffmpeg pixelformat"); + } + codec_pf = AV_PIX_FMT_YUV420P; + + swscaleobj.SetDefaults(zm_pf, codec_pf, width, height); + + /* Calculate the image sizes. We will need this for parameter checking */ + zm_imgsize = colours * width * height; + codec_imgsize = avpicture_get_size( codec_pf, width, height); + if(!codec_imgsize) { + Error("Failed calculating codec pixel format image size"); + } + + /* If supplied with user parameters to the encoder, copy them */ + if(p_user_params != NULL) { + user_params = *p_user_params; + } + + /* Setup x264 parameters */ + if(x264config() < 0) { + Error("Failed setting x264 parameters"); + } + + /* Allocate x264 input picture */ + x264_picture_alloc(&x264picin, X264_CSP_I420, x264params.i_width, x264params.i_height); + +} + +X264MP4Writer::~X264MP4Writer() { + + /* Free x264 input picture */ + x264_picture_clean(&x264picin); + + if(bOpen) + Close(); + +} + +int X264MP4Writer::Open() { + + /* Open the encoder */ + x264enc = x264_encoder_open(&x264params); + if(x264enc == NULL) { + Error("Failed opening x264 encoder"); + return -1; + } + + // Debug(4,"x264 maximum delayed frames: %d",x264_encoder_maximum_delayed_frames(x264enc)); + + x264_nal_t* nals; + int i_nals; + if(!x264_encoder_headers(x264enc,&nals,&i_nals)) { + Error("Failed getting encoder headers"); + return -2; + } + + /* Search SPS NAL for AVC information */ + for(int i=0;i 0; i-- ) { + x264encodeloop(true); + } + + /* Close the encoder */ + x264_encoder_close(x264enc); + + /* Close MP4 handle */ + MP4Close(mp4h); + + /* Required for proper HTTP streaming */ + MP4Optimize((path + ".incomplete").c_str(), path.c_str()); + + /* Delete the temporary file */ + unlink((path + ".incomplete").c_str()); + + bOpen = false; + + Debug(7, "Video closed. Total frames: %d", frame_count); + + return 0; +} + +int X264MP4Writer::Reset(const char* new_path) { + + /* Close the encoder and file */ + if(bOpen) + Close(); + + /* Reset common variables */ + VideoWriter::Reset(new_path); + + /* Reset local variables */ + bFirstFrame = true; + bGotH264AVCInfo = false; + prevnals.clear(); + prevpayload.clear(); + + /* Reset x264 parameters */ + x264config(); + + /* Open the encoder */ + Open(); + + return 0; +} + +int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) { + + /* Parameter checking */ + if(data == NULL) { + Error("NULL buffer"); + return -1; + } + + if(data_size != zm_imgsize) { + Error("The data buffer size does not match the expected size. Expected: %d Current: %d", zm_imgsize, data_size); + return -2; + } + + if(!bOpen) { + Warning("The encoder was not initialized, initializing now"); + Open(); + } + + /* Convert the image into the x264 input picture */ + if(swscaleobj.ConvertDefaults(data, data_size, x264picin.img.plane[0], codec_imgsize) < 0) { + Error("Image conversion failed"); + return -3; + } + + /* Set PTS */ + x264picin.i_pts = frame_time; + + /* Do the encoding */ + x264encodeloop(); + + /* Increment frame counter */ + frame_count++; + + return 0; +} + +int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) { + + if(img->Width() != width) { + Error("Source image width differs. Source: %d Output: %d",img->Width(), width); + return -12; + } + + if(img->Height() != height) { + Error("Source image height differs. Source: %d Output: %d",img->Height(), height); + return -13; + } + + return Encode(img->Buffer(),img->Size(),frame_time); +} + +int X264MP4Writer::x264config() { + /* Sets up the encoder configuration */ + + int x264ret; + + /* Defaults */ + const char* preset = "veryfast"; + const char* tune = "stillimage"; + const char* profile = "main"; + + /* Search the user parameters for preset, tune and profile */ + for(unsigned int i=0; i < user_params.size(); i++) { + if(strcmp(user_params[i].pname, "preset") == 0) { + /* Got preset */ + preset = user_params[i].pvalue; + } else if(strcmp(user_params[i].pname, "tune") == 0) { + /* Got tune */ + tune = user_params[i].pvalue; + } else if(strcmp(user_params[i].pname, "profile") == 0) { + /* Got profile */ + profile = user_params[i].pvalue; + } + } + + /* Set the defaults and preset and tune */ + x264ret = x264_param_default_preset(&x264params, preset, tune); + if(x264ret != 0) { + Error("Failed setting x264 preset %s and tune %s : %d",preset,tune,x264ret); + } + + /* Set the profile */ + x264ret = x264_param_apply_profile(&x264params, profile); + if(x264ret != 0) { + Error("Failed setting x264 profile %s : %d",profile,x264ret); + } + + /* Input format */ + x264params.i_width = width; + x264params.i_height = height; + x264params.i_csp = X264_CSP_I420; + + /* Quality control */ + x264params.rc.i_rc_method = X264_RC_CRF; + x264params.rc.f_rf_constant = 23.0; + + /* Enable b-frames */ + x264params.i_bframe = 16; + x264params.i_bframe_adaptive = 1; + + /* Timebase */ + x264params.i_timebase_num = 1; + x264params.i_timebase_den = 1000; + + /* Enable variable frame rate */ + x264params.b_vfr_input = 1; + + /* Disable annex-b (start codes) */ + x264params.b_annexb = 0; + + /* TODO: Setup error handler */ + //x264params.i_log_level = X264_LOG_DEBUG; + + /* Process user parameters (excluding preset, tune and profile) */ + for(unsigned int i=0; i < user_params.size(); i++) { + /* Skip preset, tune and profile */ + if( (strcmp(user_params[i].pname, "preset") == 0) || (strcmp(user_params[i].pname, "tune") == 0) || (strcmp(user_params[i].pname, "profile") == 0) ) { + continue; + } + + /* Pass the name and value to x264 */ + x264ret = x264_param_parse(&x264params, user_params[i].pname, user_params[i].pvalue); + + /* Error checking */ + if(x264ret != 0) { + if(x264ret == X264_PARAM_BAD_NAME) { + Error("Failed processing x264 user parameter %s=%s : Bad name", user_params[i].pname, user_params[i].pvalue); + } else if(x264ret == X264_PARAM_BAD_VALUE) { + Error("Failed processing x264 user parameter %s=%s : Bad value", user_params[i].pname, user_params[i].pvalue); + } else { + Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", user_params[i].pname, user_params[i].pvalue, x264ret); + } + } + } + + return 0; +} + +void X264MP4Writer::x264encodeloop(bool bFlush) { + + x264_nal_t* nals; + int i_nals; + int frame_size; + + if(bFlush) { + frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, NULL, &x264picout); + } else { + frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, &x264picin, &x264picout); + } + + if (frame_size > 0 || bFlush) { + Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",frame_count, x264picout.i_pts, x264picout.i_dts, frame_size); + + /* Handle the previous frame */ + if(!bFirstFrame) { + + buffer.clear(); + + /* Process the NALs for the previous frame */ + for(unsigned int i=0; i < prevnals.size(); i++) { + Debug(9,"Processing NAL: Type %d Size %d",prevnals[i].i_type,prevnals[i].i_payload); + + switch(prevnals[i].i_type) { + case NAL_PPS: + /* PPS NAL */ + MP4AddH264PictureParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); + break; + case NAL_SPS: + /* SPS NAL */ + MP4AddH264SequenceParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4); + break; + default: + /* Anything else, hopefully frames, so copy it into the sample */ + buffer.append(prevnals[i].p_payload, prevnals[i].i_payload); + } + } + + /* Calculate frame duration and offset */ + int duration = x264picout.i_dts - prevDTS; + int offset = prevPTS - prevDTS; + + /* Write the sample */ + if(!buffer.empty()) { + if(!MP4WriteSample(mp4h, mp4vtid, buffer.extract(buffer.size()), buffer.size(), duration, offset, prevKeyframe)) { + Error("Failed writing sample"); + } + } + + /* Cleanup */ + prevnals.clear(); + prevpayload.clear(); + + } + + /* Got a frame. Copy this new frame into the previous frame */ + if(frame_size > 0) { + /* Copy the NALs and the payloads */ + for(int i=0;i* vec) { + if(vec == NULL) { + Error("NULL Encoder parameters vector pointer"); + return -1; + } + + if(str == NULL) { + Error("NULL Encoder parameters string"); + return -2; + } + + vec->clear(); + + if(str[0] == 0) { + /* Empty */ + return 0; + } + + std::string line; + std::stringstream ss(str); + size_t valueoffset; + size_t valuelen; + unsigned int lineno = 0; + EncoderParameter_t param; + + while(std::getline(ss, line) ) { + lineno++; + + /* Remove CR if exists */ + if(line.length() >= 1 && line[line.length()-1] == '\r') { + line.erase(line.length()-1); + } + + /* Skip comments and empty lines */ + if(line.empty() || line[0] == '#') { + continue; + } + + valueoffset = line.find('='); + if(valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0) { + Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno); + continue; + } + + + if(valueoffset > (sizeof(param.pname)-1) ) { + Warning("Failed parsing encoder parameters line %d: Name too long", lineno); + continue; + } + + valuelen = line.length() - (valueoffset+1); + + if( valuelen > (sizeof(param.pvalue)-1) ) { + Warning("Failed parsing encoder parameters line %d: Value too long", lineno); + continue; + } + + /* Copy and NULL terminate */ + line.copy(param.pname, valueoffset, 0); + line.copy(param.pvalue, valuelen, valueoffset+1); + param.pname[valueoffset] = 0; + param.pvalue[valuelen] = 0; + + /* Push to the vector */ + vec->push_back(param); + + Debug(7, "Parsed encoder parameter: %s = %s", param.pname, param.pvalue); + } + + Debug(7, "Parsed %d lines", lineno); + + return 0; +} + + diff --git a/src/zm_video.h b/src/zm_video.h new file mode 100644 index 000000000..936adfeb8 --- /dev/null +++ b/src/zm_video.h @@ -0,0 +1,173 @@ +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#ifndef ZM_VIDEO_H +#define ZM_VIDEO_H + +#include "zm.h" +#include "zm_rgb.h" +#include "zm_utils.h" +#include "zm_ffmpeg.h" +#include "zm_buffer.h" + +/* +#define HAVE_LIBX264 1 +#define HAVE_LIBMP4V2 1 +#define HAVE_X264_H 1 +#define HAVE_MP4_H 1 +*/ + +#if HAVE_MP4V2_MP4V2_H +#include +#endif +#if HAVE_MP4V2_H +#include +#endif +#if HAVE_MP4_H +#include +#endif + +#if HAVE_X264_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#ifdef __cplusplus +} +#endif +#endif + +/* Structure for user parameters to the encoder */ +struct EncoderParameter_t { + char pname[48]; + char pvalue[48]; + +}; +int ParseEncoderParameters(const char* str, std::vector* vec); + +/* VideoWriter is a generic interface that ZM uses to save events as videos */ +/* It is relatively simple and the functions are pure virtual, so they must be implemented by the deriving class */ + +class VideoWriter { + +protected: + std::string container; + std::string codec; + std::string path; + unsigned int width; + unsigned int height; + unsigned int colours; + unsigned int subpixelorder; + + unsigned int frame_count; + +public: + VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder); + virtual ~VideoWriter(); + virtual int Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) = 0; + virtual int Encode(const Image* img, const unsigned int frame_time) = 0; + virtual int Open() = 0; + virtual int Close() = 0; + virtual int Reset(const char* new_path = NULL); + + const char* GetContainer() const { + return container.c_str(); + } + const char* GetCodec() const { + return codec.c_str(); + } + const char* GetPath() const { + return path.c_str(); + } + unsigned int GetWidth() const { + return width; + } + unsigned int GetHeight() const { + return height; + } + unsigned int GetColours() const { + return colours; + } + unsigned int GetSubpixelorder () const { + return subpixelorder; + } + unsigned int GetFrameCount() const { + return frame_count; + } +}; + +#if HAVE_LIBX264 && HAVE_LIBMP4V2 && HAVE_LIBAVUTIL && HAVE_LIBSWSCALE +#define ZM_HAVE_VIDEOWRITER_X264MP4 1 +class X264MP4Writer : public VideoWriter { + +protected: + + bool bOpen; + bool bGotH264AVCInfo; + bool bFirstFrame; + + /* SWScale */ + SWScale swscaleobj; + enum _AVPIXELFORMAT zm_pf; + enum _AVPIXELFORMAT codec_pf; + size_t codec_imgsize; + size_t zm_imgsize; + + /* User parameters */ + std::vector user_params; + + /* AVC Information */ + uint8_t x264_profleindication; + uint8_t x264_profilecompat; + uint8_t x264_levelindication; + + /* NALs */ + Buffer buffer; + + /* Previous frame */ + int prevPTS; + int prevDTS; + bool prevKeyframe; + Buffer prevpayload; + std::vector prevnals; + + /* Internal functions */ + int x264config(); + void x264encodeloop(bool bFlush = false); + + /* x264 objects */ + x264_t* x264enc; + x264_param_t x264params; + x264_picture_t x264picin; + x264_picture_t x264picout; + + /* MP4v2 objects */ + MP4FileHandle mp4h; + MP4TrackId mp4vtid; + + +public: + X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector* p_user_params = NULL); + ~X264MP4Writer(); + int Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time); + int Encode(const Image* img, const unsigned int frame_time); + int Open(); + int Close(); + int Reset(const char* new_path = NULL); + +}; +#endif // HAVE_LIBX264 && HAVE_LIBMP4V2 && HAVE_LIBAVUTIL && HAVE_LIBSWSCALE + +#endif // ZM_VIDEO_H diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp new file mode 100644 index 000000000..f55dcaabd --- /dev/null +++ b/src/zm_videostore.cpp @@ -0,0 +1,349 @@ +// +// ZoneMinder Video Storage Implementation +// Written by Chris Wiggins +// http://chriswiggins.co.nz +// Modification by Steve Gilvarry +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#define __STDC_FORMAT_MACROS 1 + +#include +#include +#include + +#include "zm.h" +#include "zm_videostore.h" + +extern "C"{ + #include "libavutil/time.h" +} + +VideoStore::VideoStore(const char *filename_in, const char *format_in, + AVStream *input_st, + AVStream *inpaud_st, + int64_t nStartTime) { + + AVDictionary *pmetadata = NULL; + int dsr; + + //store inputs in variables local to class + filename = filename_in; + format = format_in; + + keyframeMessage = false; + keyframeSkipNumber = 0; + + Info("Opening video storage stream %s format: %d\n", filename, format); + + //Init everything we need + int ret; + av_register_all(); + + ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename); + if ( ret < 0 ) { + Warning("Could not create video storage stream %s as no output context" + " could be assigned based on filename: %s", + filename, + av_make_error_string(ret).c_str() + ); + } + + //Couldn't deduce format from filename, trying from format name + if (!oc) { + avformat_alloc_output_context2(&oc, NULL, format, filename); + if (!oc) { + Fatal("Could not create video storage stream %s as no output context" + " could not be assigned based on filename or format %s", + filename, format); + } + } + + dsr = av_dict_set(&pmetadata, "title", "Zoneminder Security Recording", 0); + if (dsr < 0) Warning("%s:%d: title set failed", __FILE__, __LINE__ ); + + oc->metadata = pmetadata; + + fmt = oc->oformat; + + video_st = avformat_new_stream(oc, (AVCodec *)input_st->codec->codec); + if (!video_st) { + Fatal("Unable to create video out stream\n"); + } + + ret = avcodec_copy_context(video_st->codec, input_st->codec); + if (ret < 0) { + Fatal("Unable to copy input video context to output video context %s\n", + av_make_error_string(ret).c_str()); + } + + video_st->codec->codec_tag = 0; + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { + video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + + if (inpaud_st) { + + audio_st = avformat_new_stream(oc, (AVCodec *)inpaud_st->codec->codec); + if (!audio_st) { + Error("Unable to create audio out stream\n"); + audio_st = NULL; + } else { + ret = avcodec_copy_context(audio_st->codec, inpaud_st->codec); + if (ret < 0) { + Fatal("Unable to copy audio context %s\n", av_make_error_string(ret).c_str()); + } + audio_st->codec->codec_tag = 0; + if ( audio_st->codec->channels > 1 ) { + Warning("Audio isn't mono, changing it."); + audio_st->codec->channels = 1; + } + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { + audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + } + } else { + Debug(3, "No Audio output stream"); + audio_st = NULL; + } + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + ret = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,NULL,NULL); + if (ret < 0) { + Fatal("Could not open output file '%s': %s\n", filename, + av_make_error_string(ret).c_str()); + } + } + + //av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0); + //if ((ret = avformat_write_header(ctx, &opts)) < 0) { + //} + //os->ctx_inited = 1; + //avio_flush(ctx->pb); + //av_dict_free(&opts); + + /* Write the stream header, if any. */ + ret = avformat_write_header(oc, NULL); + if (ret < 0) { + zm_dump_stream_format( oc, 0, 0, 1 ); + Fatal("Error occurred when writing output file header to %s: %s\n", + filename, + av_make_error_string(ret).c_str()); + } + + prevDts = 0; + startPts = 0; + startDts = 0; + filter_in_rescale_delta_last = AV_NOPTS_VALUE; + + startTime=av_gettime()-nStartTime;//oc->start_time; + Info("VideoStore startTime=%d\n",startTime); +} // VideoStore::VideoStore + + +VideoStore::~VideoStore(){ + /* Write the trailer before close */ + if ( int rc = av_write_trailer(oc) ) { + Error("Error writing trailer %s", av_err2str( rc ) ); + } else { + Debug(3, "Sucess Writing trailer"); + } + + // I wonder if we should be closing the file first. + // I also wonder if we really need to be doing all the context allocation/de-allocation constantly, or whether we can just re-use it. Just do a file open/close/writeheader/etc. + // What if we were only doing audio recording? + if ( video_st ) { + avcodec_close(video_st->codec); + } + if (audio_st) { + avcodec_close(audio_st->codec); + } + + // WHen will be not using a file ? + if (!(fmt->flags & AVFMT_NOFILE)) { + /* Close the output file. */ + if ( int rc = avio_close(oc->pb) ) { + Error("Error closing avio %s", av_err2str( rc ) ); + } + } else { + Debug(3, "Not closing avio because we are not writing to a file."); + } + + /* free the stream */ + avformat_free_context(oc); +} + + +void VideoStore::dumpPacket( AVPacket *pkt ){ + char b[10240]; + + snprintf(b, sizeof(b), " pts: %" PRId64 ", dts: %" PRId64 ", data: %p, size: %d, sindex: %d, dflags: %04x, s-pos: %" PRId64 ", c-duration: %" PRId64 "\n" + , pkt->pts + , pkt->dts + , pkt->data + , pkt->size + , pkt->stream_index + , pkt->flags + , pkt->pos + , pkt->convergence_duration + ); + Info("%s:%d:DEBUG: %s", __FILE__, __LINE__, b); +} + +int VideoStore::writeVideoFramePacket(AVPacket *ipkt, AVStream *input_st){//, AVPacket *lastKeyframePkt){ + + int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, video_st->time_base); + + AVPacket opkt, safepkt; + AVPicture pict; + + av_init_packet(&opkt); + + //Scale the PTS of the outgoing packet to be the correct time base + if (ipkt->pts != AV_NOPTS_VALUE) { + opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, video_st->time_base) - ost_tb_start_time; + } else { + opkt.pts = AV_NOPTS_VALUE; + } + + //Scale the DTS of the outgoing packet to be the correct time base + if(ipkt->dts == AV_NOPTS_VALUE) { + opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, video_st->time_base); + } else { + opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, video_st->time_base); + } + + opkt.dts -= ost_tb_start_time; + + opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, video_st->time_base); + opkt.flags = ipkt->flags; + opkt.pos=-1; + + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.stream_index = ipkt->stream_index; + /*opkt.flags |= AV_PKT_FLAG_KEY;*/ + + if (video_st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (fmt->flags & AVFMT_RAWPICTURE)) { + /* store AVPicture in AVPacket, as expected by the output format */ + avpicture_fill(&pict, opkt.data, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height); + opkt.data = (uint8_t *)&pict; + opkt.size = sizeof(AVPicture); + opkt.flags |= AV_PKT_FLAG_KEY; + } + + memcpy(&safepkt, &opkt, sizeof(AVPacket)); + + if ((opkt.data == NULL)||(opkt.size < 1)) { + Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__ ); + dumpPacket(&opkt); + + } else if ((prevDts > 0) && (prevDts >= opkt.dts)) { + Warning("%s:%d: DTS out of order: %lld \u226E %lld; discarding frame", __FILE__, __LINE__, prevDts, opkt.dts); + prevDts = opkt.dts; + dumpPacket(&opkt); + + } else { + int ret; + + prevDts = opkt.dts; // Unsure if av_interleaved_write_frame() clobbers opkt.dts when out of order, so storing in advance + ret = av_interleaved_write_frame(oc, &opkt); + if(ret<0){ + // There's nothing we can really do if the frame is rejected, just drop it and get on with the next + Warning("%s:%d: Writing frame [av_interleaved_write_frame()] failed: %s(%d) ", __FILE__, __LINE__, av_make_error_string(ret).c_str(), (ret)); + dumpPacket(&safepkt); + } + } + + + av_free_packet(&opkt); + + return 0; + +} + +int VideoStore::writeAudioFramePacket(AVPacket *ipkt, AVStream *input_st){ + + if(!audio_st) { + Error("Called writeAudioFramePacket when no audio_st"); + return -1;//FIXME -ve return codes do not free packet in ffmpeg_camera at the moment + } + /*if(!keyframeMessage) + return -1;*/ + //zm_dump_stream_format( oc, ipkt->stream_index, 0, 1 ); + + // What is this doing? Getting the time of the start of this video chunk? Does that actually make sense? + int64_t ost_tb_start_time = av_rescale_q(startTime, AV_TIME_BASE_Q, audio_st->time_base); + + AVPacket opkt; + + av_init_packet(&opkt); + Debug(3, "after init packet" ); + + + //Scale the PTS of the outgoing packet to be the correct time base + if (ipkt->pts != AV_NOPTS_VALUE) { + Debug(3, "Rescaling output pts"); + opkt.pts = av_rescale_q(ipkt->pts-startPts, input_st->time_base, audio_st->time_base) - ost_tb_start_time; + } else { + Debug(3, "Setting output pts to AV_NOPTS_VALUE"); + opkt.pts = AV_NOPTS_VALUE; + } + + //Scale the DTS of the outgoing packet to be the correct time base + if(ipkt->dts == AV_NOPTS_VALUE) { + Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts ); + opkt.dts = av_rescale_q(input_st->cur_dts-startDts, AV_TIME_BASE_Q, audio_st->time_base); + Debug(4, "ipkt->dts == AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts ); + } else { + Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts ); + opkt.dts = av_rescale_q(ipkt->dts-startDts, input_st->time_base, audio_st->time_base); + Debug(4, "ipkt->dts != AV_NOPTS_VALUE %d to %d", AV_NOPTS_VALUE, opkt.dts ); + } + opkt.dts -= ost_tb_start_time; + + // Seems like it would be really weird for the codec type to NOT be audiu + if (audio_st->codec->codec_type == AVMEDIA_TYPE_AUDIO && ipkt->dts != AV_NOPTS_VALUE) { + Debug( 4, "code is audio, dts != AV_NOPTS_VALUE " ); + int duration = av_get_audio_frame_duration(input_st->codec, ipkt->size); + if(!duration) + duration = input_st->codec->frame_size; + + //FIXME where to get filter_in_rescale_delta_last + //FIXME av_rescale_delta doesn't exist in ubuntu vivid libavtools + opkt.dts = opkt.pts = av_rescale_delta(input_st->time_base, ipkt->dts, + (AVRational){1, input_st->codec->sample_rate}, duration, &filter_in_rescale_delta_last, + audio_st->time_base) - ost_tb_start_time; + } + + opkt.duration = av_rescale_q(ipkt->duration, input_st->time_base, audio_st->time_base); + opkt.pos=-1; + opkt.flags = ipkt->flags; + + opkt.data = ipkt->data; + opkt.size = ipkt->size; + opkt.stream_index = ipkt->stream_index; + + int ret; + ret = av_interleaved_write_frame(oc, &opkt); + if(ret!=0){ + Fatal("Error encoding audio frame packet: %s\n", av_make_error_string(ret).c_str()); + } + Debug(4,"Success writing audio frame" ); + av_free_packet(&opkt); + return 0; +} diff --git a/src/zm_videostore.h b/src/zm_videostore.h new file mode 100644 index 000000000..0d72f42b6 --- /dev/null +++ b/src/zm_videostore.h @@ -0,0 +1,51 @@ +#ifndef ZM_VIDEOSTORE_H +#define ZM_VIDEOSTORE_H + +#include "zm_ffmpeg.h" + +#if HAVE_LIBAVCODEC + +class VideoStore { +private: + + AVOutputFormat *fmt; + AVFormatContext *oc; + AVStream *video_st; + AVStream *audio_st; + + const char *filename; + const char *format; + + bool keyframeMessage; + int keyframeSkipNumber; + + int64_t startTime; + int64_t startPts; + int64_t startDts; + int64_t prevDts; + int64_t filter_in_rescale_delta_last; + +public: + VideoStore(const char *filename_in, const char *format_in, AVStream *input_st, AVStream *inpaud_st, int64_t nStartTime); + ~VideoStore(); + + int writeVideoFramePacket(AVPacket *pkt, AVStream *input_st);//, AVPacket *lastKeyframePkt); + int writeAudioFramePacket(AVPacket *pkt, AVStream *input_st); + void dumpPacket( AVPacket *pkt ); +}; + +/* +class VideoEvent { +public: + VideoEvent(unsigned int eid); + ~VideoEvent(); + + int createEventImage(unsigned int fid, char *&pBuff); + +private: + unsigned int m_eid; +};*/ + +#endif //havelibav +#endif //zm_videostore_h + diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index a152d86fc..364ea902f 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -25,7 +25,6 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ) { -<<<<<<< HEAD monitor = p_monitor; id = p_id; @@ -102,172 +101,94 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_ } pg_image->WriteJpeg( diag_path ); } -======= - monitor = p_monitor; - - id = p_id; - label = new char[strlen(p_label)+1]; - strcpy( label, p_label ); - type = p_type; - polygon = p_polygon; - alarm_rgb = p_alarm_rgb; - check_method = p_check_method; - min_pixel_threshold = p_min_pixel_threshold; - max_pixel_threshold = p_max_pixel_threshold; - min_alarm_pixels = p_min_alarm_pixels; - max_alarm_pixels = p_max_alarm_pixels; - filter_box = p_filter_box; - min_filter_pixels = p_min_filter_pixels; - max_filter_pixels = p_max_filter_pixels; - min_blob_pixels = p_min_blob_pixels; - max_blob_pixels = p_max_blob_pixels; - min_blobs = p_min_blobs; - max_blobs = p_max_blobs; - overload_frames = p_overload_frames; - extend_alarm_frames = p_extend_alarm_frames; - - Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d, AF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames, extend_alarm_frames ); - - alarmed = false; - pixel_diff = 0; - alarm_pixels = 0; - alarm_filter_pixels = 0; - alarm_blob_pixels = 0; - alarm_blobs = 0; - min_blob_size = 0; - max_blob_size = 0; - image = 0; - score = 0; - - overload_count = 0; - extend_alarm_count = 0; - - pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); - pg_image->Clear(); - pg_image->Fill( 0xff, polygon ); - pg_image->Outline( 0xff, polygon ); - - ranges = new Range[monitor->Height()]; - for ( unsigned int y = 0; y < monitor->Height(); y++) - { - ranges[y].lo_x = -1; - ranges[y].hi_x = 0; - ranges[y].off_x = 0; - const uint8_t *ppoly = pg_image->Buffer( 0, y ); - for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ ) - { - if ( *ppoly ) - { - if ( ranges[y].lo_x == -1 ) - { - ranges[y].lo_x = x; - } - if ( (unsigned int)ranges[y].hi_x < x ) - { - ranges[y].hi_x = x; - } - } - } - } - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); - } - pg_image->WriteJpeg( diag_path ); - } ->>>>>>> master } Zone::~Zone() { - delete[] label; - delete image; - delete pg_image; - delete[] ranges; + delete[] label; + delete image; + delete pg_image; + delete[] ranges; } void Zone::RecordStats( const Event *event ) { static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } //============================================================================= bool Zone::CheckOverloadCount() { - Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames); - if ( overload_count ) - { - Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - overload_count--; - return( false ); - } - return true; + Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames); + if ( overload_count ) + { + Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + overload_count--; + return( false ); + } + return true; } void Zone::SetScore(unsigned int nScore) { - score = nScore; + score = nScore; } void Zone::SetAlarmImage(const Image* srcImage) { - delete image; - image = new Image(*srcImage); + delete image; + image = new Image(*srcImage); } int Zone::GetOverloadCount() { - return overload_count; + return overload_count; } void Zone::SetOverloadCount(int nOverCount) { - overload_count = nOverCount; + overload_count = nOverCount; } int Zone::GetOverloadFrames() { - return overload_frames; + return overload_frames; } int Zone::GetExtendAlarmCount() { - return extend_alarm_count; + return extend_alarm_count; } void Zone::SetExtendAlarmCount(int nExtendAlarmCount) { - extend_alarm_count = nExtendAlarmCount; + extend_alarm_count = nExtendAlarmCount; } int Zone::GetExtendAlarmFrames() { - return extend_alarm_frames; + return extend_alarm_frames; } bool Zone::CheckExtendAlarmCount() { - Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames); - if ( extend_alarm_count ) - { - Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames ); - extend_alarm_count--; - return( true ); - } - return false; + Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames); + if ( extend_alarm_count ) + { + Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames ); + extend_alarm_count--; + return( true ); + } + return false; } @@ -277,7 +198,6 @@ bool Zone::CheckExtendAlarmCount() bool Zone::CheckAlarms( const Image *delta_image ) { -<<<<<<< HEAD ResetStats(); if ( overload_count ) @@ -730,460 +650,7 @@ bool Zone::CheckAlarms( const Image *delta_image ) diff_image->WriteJpeg( diag_path ); } Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); -======= - ResetStats(); - - if ( overload_count ) - { - Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - overload_count--; - return( false ); - } - - delete image; - // Get the difference image - Image *diff_image = image = new Image( *delta_image ); - int diff_width = diff_image->Width(); - uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); - uint8_t* pdiff; - const uint8_t* ppoly; - - unsigned int pixel_diff_count = 0; - - int alarm_lo_x = 0; - int alarm_hi_x = 0; - int alarm_lo_y = 0; - int alarm_hi_y = 0; - - int alarm_mid_x = -1; - int alarm_mid_y = -1; - - unsigned int lo_y = polygon.LoY(); - unsigned int lo_x = polygon.LoX(); - unsigned int hi_x = polygon.HiX(); - unsigned int hi_y = polygon.HiY(); - - Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); - - - Debug( 5, "Checking for alarmed pixels" ); - /* if(config.cpu_extensions && sseversion >= 20) { - sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - } else { - std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - } */ - std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); - } - diff_image->WriteJpeg( diag_path ); - } - - if ( pixel_diff_count && alarm_pixels ) - pixel_diff = pixel_diff_count/alarm_pixels; - Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); - - if( alarm_pixels ) { - if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { - /* Not enough pixels alarmed */ - return (false); - } else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { - /* Too many pixels alarmed */ - overload_count = overload_frames; - return (false); - } - } else { - /* No alarmed pixels */ - return (false); - } - - score = (100*alarm_pixels)/polygon.Area(); - if(score < 1) - score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ - Debug( 5, "Current score is %d", score ); - - if ( check_method >= FILTERED_PIXELS ) - { - int bx = filter_box.X(); - int by = filter_box.Y(); - int bx1 = bx-1; - int by1 = by-1; - - Debug( 5, "Checking for filtered pixels" ); - if ( bx > 1 || by > 1 ) - { - // Now remove any pixels smaller than our filter size - unsigned char *cpdiff; - int ldx, hdx, ldy, hdy; - bool block; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - int lo_x = ranges[y].lo_x; - int hi_x = ranges[y].hi_x; - - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); - - for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) - { - if ( *pdiff == WHITE ) - { - // Check participation in an X block - ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x; - hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1); - ldy = (y>=(lo_y+by1))?-by1:lo_y-y; - hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1); - block = false; - for ( int dy = ldy; !block && dy <= hdy; dy++ ) - { - for ( int dx = ldx; !block && dx <= hdx; dx++ ) - { - block = true; - for ( int dy2 = 0; block && dy2 < by; dy2++ ) - { - for ( int dx2 = 0; block && dx2 < bx; dx2++ ) - { - cpdiff = diff_buff + (((y+dy+dy2)*diff_width) + (x+dx+dx2)); - if ( !*cpdiff ) - { - block = false; - } - } - } - } - } - if ( !block ) - { - *pdiff = BLACK; - continue; - } - alarm_filter_pixels++; - } - } - } - } - else - { - alarm_filter_pixels = alarm_pixels; - } - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); - } - diff_image->WriteJpeg( diag_path ); - } - - Debug( 5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels ); - - if( alarm_filter_pixels ) { - if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { - /* Not enough pixels alarmed */ - return (false); - } else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { - /* Too many pixels alarmed */ - overload_count = overload_frames; - return (false); - } - } else { - /* No filtered pixels */ - return (false); - } - - score = (100*alarm_filter_pixels)/(polygon.Area()); - if(score < 1) - score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ - Debug( 5, "Current score is %d", score ); - - if ( check_method >= BLOBS ) - { - Debug( 5, "Checking for blob pixels" ); - typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; - BlobStats blob_stats[256]; - memset( blob_stats, 0, sizeof(BlobStats)*256 ); - uint8_t *spdiff; - uint8_t last_x, last_y; - BlobStats *bsx, *bsy; - BlobStats *bsm, *bss; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - int lo_x = ranges[y].lo_x; - int hi_x = ranges[y].hi_x; - - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); - for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) - { - if ( *pdiff == WHITE ) - { - Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff ); - //last_x = (x>lo_x)?*(pdiff-1):0; - //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; - last_x = 0; - if(x > 0) { - if((x-1) >= lo_x) { - last_x = *(pdiff-1); - } - } - - last_y = 0; - if(y > 0) { - if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) { - last_y = *(pdiff-diff_width); - } - } ->>>>>>> master - - if ( last_x ) - { - Debug( 9, "Left neighbour is %d", last_x ); - bsx = &blob_stats[last_x]; - if ( last_y ) - { - Debug( 9, "Top neighbour is %d", last_y ); - bsy = &blob_stats[last_y]; - if ( last_x == last_y ) - { - Debug( 9, "Matching neighbours, setting to %d", last_x ); - // Add to the blob from the x side (either side really) - *pdiff = last_x; - alarm_blob_pixels++; - bsx->count++; - if ( x > bsx->hi_x ) bsx->hi_x = x; - if ( (int)y > bsx->hi_y ) bsx->hi_y = y; - } - else - { - // Aggregate blobs - bsm = bsx->count>=bsy->count?bsx:bsy; - bss = bsm==bsx?bsy:bsx; - - Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag ); - Debug( 9, "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); - Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); - // Now change all those pixels to the other setting - int changed = 0; - for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) - { - int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x; - int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x; - - Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx ); - Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x ); - spdiff = diff_buff + ((diff_width * sy) + lo_sx); - for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) - { - Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff ); - if ( *spdiff == bss->tag ) - { - Debug( 9, "Setting pixel" ); - *spdiff = bsm->tag; - changed++; - } - } - } - *pdiff = bsm->tag; - alarm_blob_pixels++; - if ( !changed ) - { - Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); - Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); - Error( "No pixels changed, exiting" ); - exit( -1 ); - } - - // Merge the slave blob into the master - bsm->count += bss->count+1; - if ( x > bsm->hi_x ) bsm->hi_x = x; - if ( (int)y > bsm->hi_y ) bsm->hi_y = y; - if ( bss->lo_x < bsm->lo_x ) bsm->lo_x = bss->lo_x; - if ( bss->lo_y < bsm->lo_y ) bsm->lo_y = bss->lo_y; - if ( bss->hi_x > bsm->hi_x ) bsm->hi_x = bss->hi_x; - if ( bss->hi_y > bsm->hi_y ) bsm->hi_y = bss->hi_y; - - alarm_blobs--; - - Debug( 6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs ); - - // Clear out the old blob - bss->tag = 0; - bss->count = 0; - bss->lo_x = 0; - bss->lo_y = 0; - bss->hi_x = 0; - bss->hi_y = 0; - } - } - else - { - Debug( 9, "Setting to left neighbour %d", last_x ); - // Add to the blob from the x side - *pdiff = last_x; - alarm_blob_pixels++; - bsx->count++; - if ( x > bsx->hi_x ) bsx->hi_x = x; - if ( (int)y > bsx->hi_y ) bsx->hi_y = y; - } - } - else - { - if ( last_y ) - { - Debug( 9, "Top neighbour is %d", last_y ); - Debug( 9, "Setting to top neighbour %d", last_y ); - - // Add to the blob from the y side - BlobStats *bsy = &blob_stats[last_y]; - - *pdiff = last_y; - alarm_blob_pixels++; - bsy->count++; - if ( x > bsy->hi_x ) bsy->hi_x = x; - if ( (int)y > bsy->hi_y ) bsy->hi_y = y; - } - else - { - // Create a new blob - int i; - for ( i = (WHITE-1); i > 0; i-- ) - { - BlobStats *bs = &blob_stats[i]; - // See if we can recycle one first, only if it's at least two rows up - if ( bs->count && bs->hi_y < (int)(y-1) ) - { - if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) - { - if ( config.create_analysis_images || config.record_diag_images ) - { - for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) - { - spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); - for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) - { - if ( *spdiff == bs->tag ) - { - *spdiff = BLACK; - } - } - } - } - alarm_blobs--; - alarm_blob_pixels -= bs->count; - - Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - - bs->tag = 0; - bs->count = 0; - bs->lo_x = 0; - bs->lo_y = 0; - bs->hi_x = 0; - bs->hi_y = 0; - } - } - if ( !bs->count ) - { - Debug( 9, "Creating new blob %d", i ); - *pdiff = i; - alarm_blob_pixels++; - bs->tag = i; - bs->count++; - bs->lo_x = bs->hi_x = x; - bs->lo_y = bs->hi_y = y; - alarm_blobs++; - - Debug( 6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs ); - break; - } - } - if ( i == 0 ) - { - Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." ); - x = hi_x+1; - y = hi_y+1; - } - } - } - } - } - } - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); - } - diff_image->WriteJpeg( diag_path ); - } - - if ( !alarm_blobs ) - { - return( false ); - } - - Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); - - // Now eliminate blobs under the threshold - for ( int i = 1; i < WHITE; i++ ) - { - BlobStats *bs = &blob_stats[i]; - if ( bs->count ) - { - if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) - { - if ( config.create_analysis_images || config.record_diag_images ) - { - for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) - { - spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); - for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) - { - if ( *spdiff == bs->tag ) - { - *spdiff = BLACK; - } - } - } - } - alarm_blobs--; - alarm_blob_pixels -= bs->count; - - Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - - bs->tag = 0; - bs->count = 0; - bs->lo_x = 0; - bs->lo_y = 0; - bs->hi_x = 0; - bs->hi_y = 0; - } - else - { - Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count; - if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count; - } - } - } - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); - } - diff_image->WriteJpeg( diag_path ); - } - Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); - if( alarm_blobs ) { if( min_blobs && (alarm_blobs < min_blobs) ) { /* Not enough pixels alarmed */ @@ -1284,368 +751,368 @@ bool Zone::CheckAlarms( const Image *delta_image ) alarm_centre = alarm_box.Centre(); } - if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) - { + if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) + { - // First mask out anything we don't want - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - pdiff = diff_buff + ((diff_width * y) + lo_x); + // First mask out anything we don't want + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + pdiff = diff_buff + ((diff_width * y) + lo_x); - int lo_x2 = ranges[y].lo_x; - int hi_x2 = ranges[y].hi_x; + int lo_x2 = ranges[y].lo_x; + int hi_x2 = ranges[y].hi_x; - int lo_gap = lo_x2-lo_x; - if ( lo_gap > 0 ) - { - if ( lo_gap == 1 ) - { - *pdiff++ = BLACK; - } - else - { - memset( pdiff, BLACK, lo_gap ); - pdiff += lo_gap; - } - } + int lo_gap = lo_x2-lo_x; + if ( lo_gap > 0 ) + { + if ( lo_gap == 1 ) + { + *pdiff++ = BLACK; + } + else + { + memset( pdiff, BLACK, lo_gap ); + pdiff += lo_gap; + } + } - ppoly = pg_image->Buffer( lo_x2, y ); - for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) - { - if ( !*ppoly ) - { - *pdiff = BLACK; - } - } + ppoly = pg_image->Buffer( lo_x2, y ); + for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) + { + if ( !*ppoly ) + { + *pdiff = BLACK; + } + } - int hi_gap = hi_x-hi_x2; - if ( hi_gap > 0 ) - { - if ( hi_gap == 1 ) - { - *pdiff = BLACK; - } - else - { - memset( pdiff, BLACK, hi_gap ); - } - } - } - - if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { - image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); - } else { - image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); - } - - // Only need to delete this when 'image' becomes detached and points somewhere else - delete diff_image; - } - else - { - delete image; - image = 0; - } + int hi_gap = hi_x-hi_x2; + if ( hi_gap > 0 ) + { + if ( hi_gap == 1 ) + { + *pdiff = BLACK; + } + else + { + memset( pdiff, BLACK, hi_gap ); + } + } + } + + if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { + image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); + } else { + image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); + } + + // Only need to delete this when 'image' becomes detached and points somewhere else + delete diff_image; + } + else + { + delete image; + image = 0; + } - Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); - } - return( true ); + Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); + } + return( true ); } bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) { - Debug( 3, "Parsing polygon string '%s'", poly_string ); + Debug( 3, "Parsing polygon string '%s'", poly_string ); - char *str_ptr = new char[strlen(poly_string)+1]; - char *str = str_ptr; - strcpy( str, poly_string ); + char *str_ptr = new char[strlen(poly_string)+1]; + char *str = str_ptr; + strcpy( str, poly_string ); - char *ws; - int n_coords = 0; - int max_n_coords = strlen(str)/4; - Coord *coords = new Coord[max_n_coords]; - while( true ) - { - if ( *str == '\0' ) - { - break; - } - ws = strchr( str, ' ' ); - if ( ws ) - { - *ws = '\0'; - } - char *cp = strchr( str, ',' ); - if ( !cp ) - { - Error( "Bogus coordinate %s found in polygon string", str ); - delete[] coords; - delete[] str_ptr; - return( false ); - } - else - { - *cp = '\0'; - char *xp = str; - char *yp = cp+1; + char *ws; + int n_coords = 0; + int max_n_coords = strlen(str)/4; + Coord *coords = new Coord[max_n_coords]; + while( true ) + { + if ( *str == '\0' ) + { + break; + } + ws = strchr( str, ' ' ); + if ( ws ) + { + *ws = '\0'; + } + char *cp = strchr( str, ',' ); + if ( !cp ) + { + Error( "Bogus coordinate %s found in polygon string", str ); + delete[] coords; + delete[] str_ptr; + return( false ); + } + else + { + *cp = '\0'; + char *xp = str; + char *yp = cp+1; - int x = atoi(xp); - int y = atoi(yp); + int x = atoi(xp); + int y = atoi(yp); - Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); + Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); #if 0 - if ( x < 0 ) - x = 0; - else if ( x >= width ) - x = width-1; - if ( y < 0 ) - y = 0; - else if ( y >= height ) - y = height-1; + if ( x < 0 ) + x = 0; + else if ( x >= width ) + x = width-1; + if ( y < 0 ) + y = 0; + else if ( y >= height ) + y = height-1; #endif - coords[n_coords++] = Coord( x, y ); - } - if ( ws ) - str = ws+1; - else - break; - } - polygon = Polygon( n_coords, coords ); + coords[n_coords++] = Coord( x, y ); + } + if ( ws ) + str = ws+1; + else + break; + } + polygon = Polygon( n_coords, coords ); - Debug( 3, "Successfully parsed polygon string" ); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + Debug( 3, "Successfully parsed polygon string" ); + //printf( "Area: %d\n", pg.Area() ); + //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] coords; - delete[] str_ptr; + delete[] coords; + delete[] str_ptr; - return( true ); + return( true ); } bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) { - Debug( 3, "Parsing zone string '%s'", zone_string ); + Debug( 3, "Parsing zone string '%s'", zone_string ); - char *str_ptr = new char[strlen(zone_string)+1]; - char *str = str_ptr; - strcpy( str, zone_string ); + char *str_ptr = new char[strlen(zone_string)+1]; + char *str = str_ptr; + strcpy( str, zone_string ); - char *ws = strchr( str, ' ' ); - if ( !ws ) - { - Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); - } - zone_id = strtol( str, 0, 10 ); - Debug( 3, "Got zone %d from zone string", zone_id ); - if ( !ws ) - { - delete str_ptr; - return( true ); - } + char *ws = strchr( str, ' ' ); + if ( !ws ) + { + Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); + } + zone_id = strtol( str, 0, 10 ); + Debug( 3, "Got zone %d from zone string", zone_id ); + if ( !ws ) + { + delete str_ptr; + return( true ); + } - *ws = '\0'; - str = ws+1; + *ws = '\0'; + str = ws+1; - ws = strchr( str, ' ' ); - if ( !ws ) - { - Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); - } - colour = strtol( str, 0, 16 ); - Debug( 3, "Got colour %06x from zone string", colour ); - if ( !ws ) - { - delete str_ptr; - return( true ); - } - *ws = '\0'; - str = ws+1; + ws = strchr( str, ' ' ); + if ( !ws ) + { + Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); + } + colour = strtol( str, 0, 16 ); + Debug( 3, "Got colour %06x from zone string", colour ); + if ( !ws ) + { + delete str_ptr; + return( true ); + } + *ws = '\0'; + str = ws+1; - bool result = ParsePolygonString( str, polygon ); + bool result = ParsePolygonString( str, polygon ); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + //printf( "Area: %d\n", pg.Area() ); + //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] str_ptr; + delete[] str_ptr; - return( result ); + return( result ); } int Zone::Load( Monitor *monitor, Zone **&zones ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_zones = mysql_num_rows( result ); - Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); - delete[] zones; - zones = new Zone *[n_zones]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_zones = mysql_num_rows( result ); + Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); + delete[] zones; + zones = new Zone *[n_zones]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; - int Id = atoi(dbrow[col++]); - const char *Name = dbrow[col++]; - int Type = atoi(dbrow[col++]); - const char *Units = dbrow[col++]; - const char *Coords = dbrow[col++]; - int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++; - int CheckMethod = atoi(dbrow[col++]); - int MinPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int FilterX = dbrow[col]?atoi(dbrow[col]):0; col++; - int FilterY = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; - int OverloadFrames = dbrow[col]?atoi(dbrow[col]):0; col++; - int ExtendAlarmFrames = dbrow[col]?atoi(dbrow[col]):0; col++; - - /* HTML colour code is actually BGR in memory, we want RGB */ - AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); + int Id = atoi(dbrow[col++]); + const char *Name = dbrow[col++]; + int Type = atoi(dbrow[col++]); + const char *Units = dbrow[col++]; + const char *Coords = dbrow[col++]; + int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++; + int CheckMethod = atoi(dbrow[col++]); + int MinPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int FilterX = dbrow[col]?atoi(dbrow[col]):0; col++; + int FilterY = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; + int OverloadFrames = dbrow[col]?atoi(dbrow[col]):0; col++; + int ExtendAlarmFrames = dbrow[col]?atoi(dbrow[col]):0; col++; + + /* HTML colour code is actually BGR in memory, we want RGB */ + AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); - Debug( 5, "Parsing polygon %s", Coords ); - Polygon polygon; - if ( !ParsePolygonString( Coords, polygon ) ) { - Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); - continue; - } - - if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() - || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { - Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); - continue; - } - - if ( false && !strcmp( Units, "Percent" ) ) - { - MinAlarmPixels = (MinAlarmPixels*polygon.Area())/100; - MaxAlarmPixels = (MaxAlarmPixels*polygon.Area())/100; - MinFilterPixels = (MinFilterPixels*polygon.Area())/100; - MaxFilterPixels = (MaxFilterPixels*polygon.Area())/100; - MinBlobPixels = (MinBlobPixels*polygon.Area())/100; - MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100; - } - - if ( atoi(dbrow[2]) == Zone::INACTIVE ) - { - zones[i] = new Zone( monitor, Id, Name, polygon ); - } - else if ( atoi(dbrow[2]) == Zone::PRIVACY ) - { - zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon ); + Debug( 5, "Parsing polygon %s", Coords ); + Polygon polygon; + if ( !ParsePolygonString( Coords, polygon ) ) { + Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); + continue; } - { - zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames ); - } - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - return( n_zones ); + + if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() + || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { + Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); + continue; + } + + if ( false && !strcmp( Units, "Percent" ) ) + { + MinAlarmPixels = (MinAlarmPixels*polygon.Area())/100; + MaxAlarmPixels = (MaxAlarmPixels*polygon.Area())/100; + MinFilterPixels = (MinFilterPixels*polygon.Area())/100; + MaxFilterPixels = (MaxFilterPixels*polygon.Area())/100; + MinBlobPixels = (MinBlobPixels*polygon.Area())/100; + MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100; + } + + if ( atoi(dbrow[2]) == Zone::INACTIVE ) + { + zones[i] = new Zone( monitor, Id, Name, polygon ); + } + else if ( atoi(dbrow[2]) == Zone::PRIVACY ) + { + zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon ); + } + { + zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames ); + } + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); + return( n_zones ); } bool Zone::DumpSettings( char *output, bool /*verbose*/ ) { - output[0] = 0; + output[0] = 0; - sprintf( output+strlen(output), " Id : %d\n", id ); - sprintf( output+strlen(output), " Label : %s\n", label ); - sprintf( output+strlen(output), " Type: %d - %s\n", type, - type==ACTIVE?"Active":( - type==INCLUSIVE?"Inclusive":( - type==EXCLUSIVE?"Exclusive":( - type==PRECLUSIVE?"Preclusive":( - type==INACTIVE?"Inactive":( - type==PRIVACY?"Privacy":"Unknown" - )))))); - sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); - for ( int i = 0; i < polygon.getNumCoords(); i++ ) - { - sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); - } - sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); - sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, - check_method==ALARMED_PIXELS?"Alarmed Pixels":( - check_method==FILTERED_PIXELS?"FilteredPixels":( - check_method==BLOBS?"Blobs":"Unknown" - ))); - sprintf( output+strlen(output), " Min Pixel Threshold : %d\n", min_pixel_threshold ); - sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); - sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); - sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); - sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); - sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); - sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); - sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); - sprintf( output+strlen(output), " Max Blob Pixels : %d\n", max_blob_pixels ); - sprintf( output+strlen(output), " Min Blobs : %d\n", min_blobs ); - sprintf( output+strlen(output), " Max Blobs : %d\n", max_blobs ); - return( true ); + sprintf( output+strlen(output), " Id : %d\n", id ); + sprintf( output+strlen(output), " Label : %s\n", label ); + sprintf( output+strlen(output), " Type: %d - %s\n", type, + type==ACTIVE?"Active":( + type==INCLUSIVE?"Inclusive":( + type==EXCLUSIVE?"Exclusive":( + type==PRECLUSIVE?"Preclusive":( + type==INACTIVE?"Inactive":( + type==PRIVACY?"Privacy":"Unknown" + )))))); + sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); + for ( int i = 0; i < polygon.getNumCoords(); i++ ) + { + sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); + } + sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); + sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, + check_method==ALARMED_PIXELS?"Alarmed Pixels":( + check_method==FILTERED_PIXELS?"FilteredPixels":( + check_method==BLOBS?"Blobs":"Unknown" + ))); + sprintf( output+strlen(output), " Min Pixel Threshold : %d\n", min_pixel_threshold ); + sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); + sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); + sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); + sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); + sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); + sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); + sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); + sprintf( output+strlen(output), " Max Blob Pixels : %d\n", max_blob_pixels ); + sprintf( output+strlen(output), " Min Blobs : %d\n", min_blobs ); + sprintf( output+strlen(output), " Max Blobs : %d\n", max_blobs ); + return( true ); } void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) { - uint32_t pixelsalarmed = 0; - uint32_t pixelsdifference = 0; - uint8_t *pdiff; - const uint8_t *ppoly; - uint8_t calc_max_pixel_threshold = 255; - unsigned int lo_y; - unsigned int hi_y; - unsigned int lo_x; - unsigned int hi_x; - - if(max_pixel_threshold) - calc_max_pixel_threshold = max_pixel_threshold; - - lo_y = polygon.LoY(); - hi_y = polygon.HiY(); - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - lo_x = ranges[y].lo_x; - hi_x = ranges[y].hi_x; - - Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); - pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); - ppoly = ppoly_image->Buffer( lo_x, y ); - - for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) - { - if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) - { - pixelsalarmed++; - pixelsdifference += *pdiff; - *pdiff = WHITE; - } - else - { - *pdiff = BLACK; - } - } - } - - /* Store the results */ - *pixel_count = pixelsalarmed; - *pixel_sum = pixelsdifference; + uint32_t pixelsalarmed = 0; + uint32_t pixelsdifference = 0; + uint8_t *pdiff; + const uint8_t *ppoly; + uint8_t calc_max_pixel_threshold = 255; + unsigned int lo_y; + unsigned int hi_y; + unsigned int lo_x; + unsigned int hi_x; + + if(max_pixel_threshold) + calc_max_pixel_threshold = max_pixel_threshold; + + lo_y = polygon.LoY(); + hi_y = polygon.HiY(); + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + lo_x = ranges[y].lo_x; + hi_x = ranges[y].hi_x; + + Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); + pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); + ppoly = ppoly_image->Buffer( lo_x, y ); + + for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) + { + if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) + { + pixelsalarmed++; + pixelsdifference += *pdiff; + *pdiff = WHITE; + } + else + { + *pdiff = BLACK; + } + } + } + + /* Store the results */ + *pixel_count = pixelsalarmed; + *pixel_sum = pixelsdifference; Debug( 7, "STORED"); } diff --git a/src/zmf.cpp b/src/zmf.cpp index fafbe7de9..ebab8d4d2 100644 --- a/src/zmf.cpp +++ b/src/zmf.cpp @@ -42,9 +42,9 @@ them itself. =head1 OPTIONS - -m, --monitor_id - ID of the monitor to use - -h, --help - Display usage information - -v, --version - Print the installed version of ZoneMinder + -m, --monitor_id - ID of the monitor to use + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder =cut @@ -73,237 +73,71 @@ them itself. int OpenSocket( int monitor_id ) { - int sd = socket( AF_UNIX, SOCK_STREAM, 0); - if ( sd < 0 ) - { - Error( "Can't create socket: %s", strerror(errno) ); - return( -1 ); - } + int sd = socket( AF_UNIX, SOCK_STREAM, 0); + if ( sd < 0 ) + { + Error( "Can't create socket: %s", strerror(errno) ); + return( -1 ); + } - char sock_path[PATH_MAX] = ""; - snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); - if ( unlink( sock_path ) < 0 ) - { - Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); - } + char sock_path[PATH_MAX] = ""; + snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); + if ( unlink( sock_path ) < 0 ) + { + Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); + } - struct sockaddr_un addr; + struct sockaddr_un addr; - strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); - addr.sun_family = AF_UNIX; + strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); + addr.sun_family = AF_UNIX; - if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) - { - Error( "Can't bind: %s", strerror(errno) ); - exit( -1 ); - } + if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) + { + Error( "Can't bind: %s", strerror(errno) ); + exit( -1 ); + } - if ( listen( sd, SOMAXCONN ) < 0 ) - { - Error( "Can't listen: %s", strerror(errno) ); - return( -1 ); - } + if ( listen( sd, SOMAXCONN ) < 0 ) + { + Error( "Can't listen: %s", strerror(errno) ); + return( -1 ); + } - struct sockaddr_un rem_addr; - socklen_t rem_addr_len = sizeof(rem_addr); - int new_sd = -1; - if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) - { - Error( "Can't accept: %s", strerror(errno) ); - exit( -1 ); - } - close( sd ); + struct sockaddr_un rem_addr; + socklen_t rem_addr_len = sizeof(rem_addr); + int new_sd = -1; + if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) + { + Error( "Can't accept: %s", strerror(errno) ); + exit( -1 ); + } + close( sd ); - sd = new_sd; + sd = new_sd; - Info( "Frame server socket open, awaiting images" ); - return( sd ); + Info( "Frame server socket open, awaiting images" ); + return( sd ); } int ReopenSocket( int &sd, int monitor_id ) { - close( sd ); - return( sd = OpenSocket( monitor_id ) ); + close( sd ); + return( sd = OpenSocket( monitor_id ) ); } void Usage() { - fprintf( stderr, "zmf -m \n" ); - fprintf( stderr, "Options:\n" ); - fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); - exit( 0 ); + fprintf( stderr, "zmf -m \n" ); + fprintf( stderr, "Options:\n" ); + fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); + exit( 0 ); } int main( int argc, char *argv[] ) { -<<<<<<< HEAD - self = argv[0]; - - srand( getpid() * time( 0 ) ); - - int id = -1; - - static struct option long_options[] = { - {"monitor", 1, 0, 'm'}, - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, - {0, 0, 0, 0} - }; - - while (1) - { - int option_index = 0; - - int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); - if (c == -1) - { - break; - } - - switch (c) - { - case 'm': - id = atoi(optarg); - break; - case 'h': - case '?': - Usage(); - break; - case 'v': - std::cout << ZM_VERSION << "\n"; - exit(0); - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } - - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - Usage(); - } - - if ( id < 0 ) - { - fprintf( stderr, "Bogus monitor %d\n", id ); - Usage(); - exit( 0 ); - } - - char log_id_string[16]; - snprintf( log_id_string, sizeof(log_id_string), "m%d", id ); - - zmLoadConfig(); - - logInit( "zmf" ); - - ssedetect(); - - Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY ); - - if ( !monitor ) - { - fprintf( stderr, "Can't find monitor with id of %d\n", id ); - exit( -1 ); - } - Storage *Storage = monitor->getStorage(); - - char capt_path[PATH_MAX]; - char anal_path[PATH_MAX]; - snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", Storage->Path(), monitor->Id(), config.event_image_digits ); - snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", Storage->Path(), monitor->Id(), config.event_image_digits ); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); - - sigset_t block_set; - sigemptyset( &block_set ); - - int sd = OpenSocket( monitor->Id() ); - - FrameHeader frame_header = { 0, 0, false, 0 }; - //unsigned char *image_data = 0; - - fd_set rfds; - - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - while( 1 ) - { - struct timeval temp_timeout = timeout; - - FD_ZERO(&rfds); - FD_SET(sd, &rfds); - int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); - if( n_found == 0 ) - { - Debug( 1, "Select timed out" ); - continue; - } - else if ( n_found < 0) - { - Error( "Select error: %s", strerror(errno) ); - ReopenSocket( sd, monitor->Id() ); - continue; - } - - sigprocmask( SIG_BLOCK, &block_set, 0 ); - - int n_bytes = read( sd, &frame_header, sizeof(frame_header) ); - if ( n_bytes != sizeof(frame_header) ) - { - if ( n_bytes < 0 ) - { - Error( "Can't read frame header: %s", strerror(errno) ); - } - else if ( n_bytes > 0 ) - { - Error( "Incomplete read of frame header, %d bytes only", n_bytes ); - } - else - { - Warning( "Socket closed at remote end" ); - } - ReopenSocket( sd, monitor->Id() ); - continue; - } - Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); - static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; - - // Read for pipe and loop until bytes expected have been read or an error occurs - int bytes_read = 0; - do - { - n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); - if (n_bytes < 0) break; // break on error - if (n_bytes < (int)frame_header.image_length) - { - // print some informational messages - if (bytes_read == 0) - { - Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); - } - else if (bytes_read+n_bytes == (int)frame_header.image_length) - { - Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); - } - else - { - Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); - } - } - bytes_read+= n_bytes; - } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); - - // Print errors if there was a problem - if ( n_bytes < 1 ) -======= self = argv[0]; srand( getpid() * time( 0 ) ); @@ -377,11 +211,12 @@ int main( int argc, char *argv[] ) fprintf( stderr, "Can't find monitor with id of %d\n", id ); exit( -1 ); } + Storage *Storage = monitor->getStorage(); char capt_path[PATH_MAX]; char anal_path[PATH_MAX]; - snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); - snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); + snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", Storage->Path(), monitor->Id(), config.event_image_digits ); + snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", Storage->Path(), monitor->Id(), config.event_image_digits ); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); @@ -440,78 +275,78 @@ int main( int argc, char *argv[] ) Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; - // Read for pipe and loop until bytes expected have been read or an error occurs - int bytes_read = 0; - do - { - n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); - if (n_bytes < 0) break; // break on error - if (n_bytes < (int)frame_header.image_length) - { - // print some informational messages - if (bytes_read == 0) ->>>>>>> master - { - Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); - } - else if (bytes_read+n_bytes == (int)frame_header.image_length) - { - Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); - } - else - { - Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); - } - } - bytes_read+= n_bytes; - } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); + // Read for pipe and loop until bytes expected have been read or an error occurs + int bytes_read = 0; + do + { + n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); + if (n_bytes < 0) break; // break on error + if (n_bytes < (int)frame_header.image_length) + { + // print some informational messages + if (bytes_read == 0) + { + Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); + } + else if (bytes_read+n_bytes == (int)frame_header.image_length) + { + Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); + } + else + { + Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); + } + } + bytes_read+= n_bytes; + } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); - // Print errors if there was a problem - if ( n_bytes < 1 ) - { - Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); - if ( n_bytes < 0 ) - { - Error( "Can't read frame image data: %s", strerror(errno) ); - } - else - { - Warning( "Socket closed at remote end" ); - } - ReopenSocket( sd, monitor->Id() ); - continue; - } + // Print errors if there was a problem + if ( n_bytes < 1 ) + { - static char subpath[PATH_MAX] = ""; - if ( config.use_deep_storage ) - { - struct tm *time = localtime( &frame_header.event_time ); - snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); - } - else - { - snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); - } + Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); + if ( n_bytes < 0 ) + { + Error( "Can't read frame image data: %s", strerror(errno) ); + } + else + { + Warning( "Socket closed at remote end" ); + } + ReopenSocket( sd, monitor->Id() ); + continue; + } - static char path[PATH_MAX] = ""; - snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); - Debug( 1, "Got image, writing to %s", path ); + static char subpath[PATH_MAX] = ""; + if ( config.use_deep_storage ) + { + struct tm *time = localtime( &frame_header.event_time ); + snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); + } + else + { + snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); + } - FILE *fd = 0; - if ( (fd = fopen( path, "w" )) < 0 ) - { - Error( "Can't fopen '%s': %s", path, strerror(errno) ); - exit( -1 ); - } - if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) - { - Error( "Can't fwrite image data: %s", strerror(errno) ); - exit( -1 ); - } - fclose( fd ); + static char path[PATH_MAX] = ""; + snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); + Debug( 1, "Got image, writing to %s", path ); - sigprocmask( SIG_UNBLOCK, &block_set, 0 ); - } - logTerm(); - zmDbClose(); + FILE *fd = 0; + if ( (fd = fopen( path, "w" )) < 0 ) + { + Error( "Can't fopen '%s': %s", path, strerror(errno) ); + exit( -1 ); + } + if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) + { + Error( "Can't fwrite image data: %s", strerror(errno) ); + exit( -1 ); + } + fclose( fd ); + + sigprocmask( SIG_UNBLOCK, &block_set, 0 ); + } + logTerm(); + zmDbClose(); } diff --git a/src/zms.cpp b/src/zms.cpp index e40fcdf95..ca38926ac 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -28,318 +28,321 @@ bool ValidateAccess( User *user, int mon_id ) { - bool allowed = true; + bool allowed = true; - if ( mon_id > 0 ) - { - if ( user->getStream() < User::PERM_VIEW ) - allowed = false; - if ( !user->canAccess( mon_id ) ) - allowed = false; - } - else - { - if ( user->getEvents() < User::PERM_VIEW ) - allowed = false; - } - if ( !allowed ) - { - Error( "Error, insufficient privileges for requested action" ); - exit( -1 ); - } - return( allowed ); + if ( mon_id > 0 ) + { + if ( user->getStream() < User::PERM_VIEW ) + allowed = false; + if ( !user->canAccess( mon_id ) ) + allowed = false; + } + else + { + if ( user->getEvents() < User::PERM_VIEW ) + allowed = false; + } + if ( !allowed ) + { + Error( "Error, insufficient privileges for requested action" ); + exit( -1 ); + } + return( allowed ); } int main( int argc, const char *argv[] ) { - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; - enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; - char format[32] = ""; - int monitor_id = 0; - time_t event_time = 0; - int event_id = 0; - unsigned int frame_id = 1; - unsigned int scale = 100; - unsigned int rate = 100; - double maxfps = 10.0; - unsigned int bitrate = 100000; - unsigned int ttl = 0; - EventStream::StreamMode replay = EventStream::MODE_SINGLE; - char username[64] = ""; - char password[64] = ""; - char auth[64] = ""; - unsigned int connkey = 0; - unsigned int playback_buffer = 0; + enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; + enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; + char format[32] = ""; + int monitor_id = 0; + time_t event_time = 0; + int event_id = 0; + unsigned int frame_id = 1; + unsigned int scale = 100; + unsigned int rate = 100; + double maxfps = 10.0; + unsigned int bitrate = 100000; + unsigned int ttl = 0; + EventStream::StreamMode replay = EventStream::MODE_SINGLE; + char username[64] = ""; + char password[64] = ""; + char auth[64] = ""; + unsigned int connkey = 0; + unsigned int playback_buffer = 0; - bool nph = false; - const char *basename = strrchr( argv[0], '/' ); - if (basename) //if we found a / lets skip past it - basename++; - else //argv[0] will not always contain the full path, but rather just the script name - basename = argv[0]; - const char *nph_prefix = "nph-"; - if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) - { - nph = true; - } - - zmLoadConfig(); + bool nph = false; + const char *basename = strrchr( argv[0], '/' ); + if (basename) //if we found a / lets skip past it + basename++; + else //argv[0] will not always contain the full path, but rather just the script name + basename = argv[0]; + const char *nph_prefix = "nph-"; + if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) + { + nph = true; + } + + zmLoadConfig(); - logInit( "zms" ); - - ssedetect(); + logInit( "zms" ); + + ssedetect(); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - const char *query = getenv( "QUERY_STRING" ); - if ( query ) - { - Debug( 1, "Query: %s", query ); - - char temp_query[1024]; - strncpy( temp_query, query, sizeof(temp_query) ); - char *q_ptr = temp_query; - char *parms[16]; // Shouldn't be more than this - int parm_no = 0; - while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) - { - parm_no++; - q_ptr = NULL; - } - - for ( int p = 0; p < parm_no; p++ ) - { - char *name = strtok( parms[p], "=" ); - char *value = strtok( NULL, "=" ); - if ( !value ) - value = (char *)""; - if ( !strcmp( name, "source" ) ) - { - source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; - } - else if ( !strcmp( name, "mode" ) ) - { - mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; - mode = !strcmp( value, "raw" )?ZMS_RAW:mode; - mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; - mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; - } - else if ( !strcmp( name, "format" ) ) - strncpy( format, value, sizeof(format) ); - else if ( !strcmp( name, "monitor" ) ) - monitor_id = atoi( value ); - else if ( !strcmp( name, "time" ) ) - event_time = atoi( value ); - else if ( !strcmp( name, "event" ) ) - event_id = strtoull( value, (char **)NULL, 10 ); - else if ( !strcmp( name, "frame" ) ) - frame_id = strtoull( value, (char **)NULL, 10 ); - else if ( !strcmp( name, "scale" ) ) - scale = atoi( value ); - else if ( !strcmp( name, "rate" ) ) - rate = atoi( value ); - else if ( !strcmp( name, "maxfps" ) ) - maxfps = atof( value ); - else if ( !strcmp( name, "bitrate" ) ) - bitrate = atoi( value ); - else if ( !strcmp( name, "ttl" ) ) - ttl = atoi(value); - else if ( !strcmp( name, "replay" ) ) - { - replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE; - replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay; - } - else if ( !strcmp( name, "connkey" ) ) - connkey = atoi(value); - else if ( !strcmp( name, "buffer" ) ) - playback_buffer = atoi(value); - else if ( config.opt_use_auth ) - { - if ( strcmp( config.auth_relay, "none" ) == 0 ) + const char *query = getenv( "QUERY_STRING" ); + if ( query ) + { + Debug( 1, "Query: %s", query ); + + char temp_query[1024]; + strncpy( temp_query, query, sizeof(temp_query) ); + char *q_ptr = temp_query; + char *parms[16]; // Shouldn't be more than this + int parm_no = 0; + while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) + { + parm_no++; + q_ptr = NULL; + } + + for ( int p = 0; p < parm_no; p++ ) + { + char *name = strtok( parms[p], "=" ); + char *value = strtok( NULL, "=" ); + if ( !value ) + value = (char *)""; + if ( !strcmp( name, "source" ) ) + { + source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; + } + else if ( !strcmp( name, "mode" ) ) + { + mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; + mode = !strcmp( value, "raw" )?ZMS_RAW:mode; + mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; + mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; + } + else if ( !strcmp( name, "format" ) ) + strncpy( format, value, sizeof(format) ); + else if ( !strcmp( name, "monitor" ) ) + monitor_id = atoi( value ); + else if ( !strcmp( name, "time" ) ) + event_time = atoi( value ); + else if ( !strcmp( name, "event" ) ) + event_id = strtoull( value, (char **)NULL, 10 ); + else if ( !strcmp( name, "frame" ) ) + frame_id = strtoull( value, (char **)NULL, 10 ); + else if ( !strcmp( name, "scale" ) ) + scale = atoi( value ); + else if ( !strcmp( name, "rate" ) ) + rate = atoi( value ); + else if ( !strcmp( name, "maxfps" ) ) + maxfps = atof( value ); + else if ( !strcmp( name, "bitrate" ) ) + bitrate = atoi( value ); + else if ( !strcmp( name, "ttl" ) ) + ttl = atoi(value); + else if ( !strcmp( name, "replay" ) ) + { + replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE; + replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay; + } + else if ( !strcmp( name, "connkey" ) ) + connkey = atoi(value); + else if ( !strcmp( name, "buffer" ) ) + playback_buffer = atoi(value); + else if ( config.opt_use_auth ) + { + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( !strcmp( name, "user" ) ) + { + strncpy( username, value, sizeof(username) ); + } + } + else + { + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( !strcmp( name, "auth" ) ) + { + strncpy( auth, value, sizeof(auth) ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( !strcmp( name, "user" ) ) + { + strncpy( username, value, sizeof(username) ); + } + if ( !strcmp( name, "pass" ) ) + { + strncpy( password, value, sizeof(password) ); + } + } + } + } + } + } + + if ( config.opt_use_auth ) + { + User *user = 0; + + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( *username ) + { + user = zmLoadUser( username ); + } + } + else + { + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( *auth ) + { + user = zmLoadAuthUser( auth, config.auth_hash_ips ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( *username && *password ) + { + user = zmLoadUser( username, password ); + } + } + } + if ( !user ) + { + Error( "Unable to authenticate user" ); + logTerm(); + zmDbClose(); + return( -1 ); + } + ValidateAccess( user, monitor_id ); + } + + setbuf( stdout, 0 ); + if ( nph ) + { + fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); + } + fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); + + time_t now = time( 0 ); + char date_string[64]; + strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) ); + + fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" ); + fprintf( stdout, "Last-Modified: %s\r\n", date_string ); + fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" ); + fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" ); + fprintf( stdout, "Pragma: no-cache\r\n"); + // Removed as causing more problems than it fixed. + //if ( !nph ) + //{ + //fprintf( stdout, "Content-Length: 0\r\n"); + //} + + if ( source == ZMS_MONITOR ) + { + MonitorStream stream; + stream.setStreamScale( scale ); + stream.setStreamReplayRate( rate ); + stream.setStreamMaxFPS( maxfps ); + stream.setStreamTTL( ttl ); + stream.setStreamQueue( connkey ); + stream.setStreamBuffer( playback_buffer ); + if ( ! stream.setStreamStart( monitor_id ) ) { + Error( "Unable to connect to zmc process for monitor %d", monitor_id ); + fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." ); + logTerm(); + zmDbClose(); + return( -1 ); + } + + if ( mode == ZMS_JPEG ) { - if ( !strcmp( name, "user" ) ) - { - strncpy( username, value, sizeof(username) ); - } + stream.setStreamType( MonitorStream::STREAM_JPEG ); + } + else if ( mode == ZMS_RAW ) + { + stream.setStreamType( MonitorStream::STREAM_RAW ); + } + else if ( mode == ZMS_ZIP ) + { + stream.setStreamType( MonitorStream::STREAM_ZIP ); + } + else if ( mode == ZMS_SINGLE ) + { + stream.setStreamType( MonitorStream::STREAM_SINGLE ); } else { - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( !strcmp( name, "auth" ) ) - { - strncpy( auth, value, sizeof(auth) ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( !strcmp( name, "user" ) ) - { - strncpy( username, value, sizeof(username) ); - } - if ( !strcmp( name, "pass" ) ) - { - strncpy( password, value, sizeof(password) ); - } - } - } - } - } - } - - if ( config.opt_use_auth ) - { - User *user = 0; - - if ( strcmp( config.auth_relay, "none" ) == 0 ) - { - if ( *username ) - { - user = zmLoadUser( username ); - } - } - else - { - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( *auth ) - { - user = zmLoadAuthUser( auth, config.auth_hash_ips ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( *username && *password ) - { - user = zmLoadUser( username, password ); - } - } - } - if ( !user ) - { - Error( "Unable to authenticate user" ); - logTerm(); - zmDbClose(); - return( -1 ); - } - ValidateAccess( user, monitor_id ); - } - - setbuf( stdout, 0 ); - if ( nph ) - { - fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); - } - fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); - - time_t now = time( 0 ); - char date_string[64]; - strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) ); - - fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" ); - fprintf( stdout, "Last-Modified: %s\r\n", date_string ); - fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" ); - fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" ); - fprintf( stdout, "Pragma: no-cache\r\n"); - // Removed as causing more problems than it fixed. - //if ( !nph ) - //{ - //fprintf( stdout, "Content-Length: 0\r\n"); - //} - - if ( source == ZMS_MONITOR ) - { - MonitorStream stream; - stream.setStreamScale( scale ); - stream.setStreamReplayRate( rate ); - stream.setStreamMaxFPS( maxfps ); - stream.setStreamTTL( ttl ); - stream.setStreamQueue( connkey ); - stream.setStreamBuffer( playback_buffer ); - if ( ! stream.setStreamStart( monitor_id ) ) { - Error( "Unable to connect to zmc process for monitor %d", monitor_id ); - fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." ); - logTerm(); - zmDbClose(); - return( -1 ); - } - - if ( mode == ZMS_JPEG ) - { - stream.setStreamType( MonitorStream::STREAM_JPEG ); - } - else if ( mode == ZMS_RAW ) - { - stream.setStreamType( MonitorStream::STREAM_RAW ); - } - else if ( mode == ZMS_ZIP ) - { - stream.setStreamType( MonitorStream::STREAM_ZIP ); - } - else if ( mode == ZMS_SINGLE ) - { - stream.setStreamType( MonitorStream::STREAM_SINGLE ); - } - else - { #if HAVE_LIBAVCODEC - stream.setStreamFormat( format ); - stream.setStreamBitrate( bitrate ); - stream.setStreamType( MonitorStream::STREAM_MPEG ); + stream.setStreamFormat( format ); + stream.setStreamBitrate( bitrate ); + stream.setStreamType( MonitorStream::STREAM_MPEG ); #else // HAVE_LIBAVCODEC - Error( "MPEG streaming of '%s' attempted while disabled", query ); - fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" ); - logTerm(); - zmDbClose(); - return( -1 ); + Error( "MPEG streaming of '%s' attempted while disabled", query ); + fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" ); + logTerm(); + zmDbClose(); + return( -1 ); #endif // HAVE_LIBAVCODEC - } - stream.runStream(); - } - else if ( source == ZMS_EVENT ) - { - EventStream stream; - stream.setStreamScale( scale ); - stream.setStreamReplayRate( rate ); - stream.setStreamMaxFPS( maxfps ); - stream.setStreamMode( replay ); - stream.setStreamQueue( connkey ); - if ( monitor_id && event_time ) - { - stream.setStreamStart( monitor_id, event_time ); - } - else - { - stream.setStreamStart( event_id, frame_id ); - } - if ( mode == ZMS_JPEG ) - { - stream.setStreamType( EventStream::STREAM_JPEG ); - } - else + } + stream.runStream(); + } + else if ( source == ZMS_EVENT ) { + if ( ! event_id ) { + Fatal( "Can't view an event without specifying an event_id." ); + } + EventStream stream; + stream.setStreamScale( scale ); + stream.setStreamReplayRate( rate ); + stream.setStreamMaxFPS( maxfps ); + stream.setStreamMode( replay ); + stream.setStreamQueue( connkey ); + if ( monitor_id && event_time ) + { + stream.setStreamStart( monitor_id, event_time ); + } + else + { + stream.setStreamStart( event_id, frame_id ); + } + if ( mode == ZMS_JPEG ) + { + stream.setStreamType( EventStream::STREAM_JPEG ); + } + else + { #if HAVE_LIBAVCODEC - stream.setStreamFormat( format ); - stream.setStreamBitrate( bitrate ); - stream.setStreamType( EventStream::STREAM_MPEG ); + stream.setStreamFormat( format ); + stream.setStreamBitrate( bitrate ); + stream.setStreamType( EventStream::STREAM_MPEG ); #else // HAVE_LIBAVCODEC - Error( "MPEG streaming of '%s' attempted while disabled", query ); - fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" ); - logTerm(); - zmDbClose(); - return( -1 ); + Error( "MPEG streaming of '%s' attempted while disabled", query ); + fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" ); + logTerm(); + zmDbClose(); + return( -1 ); #endif // HAVE_LIBAVCODEC + } + stream.runStream(); } - stream.runStream(); - } - logTerm(); - zmDbClose(); + logTerm(); + zmDbClose(); - return( 0 ); + return( 0 ); } diff --git a/src/zmu.cpp b/src/zmu.cpp index 0c5a5a41e..21cbe19aa 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -41,46 +41,46 @@ problems. =head1 OPTIONS General options: - -v, --verbose - Produce more verbose output - -l, --list - List the current status of active (or all with -v) monitors - -h, --help - Display usage information - -v, --version - Print the installed version of ZoneMinder + -v, --verbose - Produce more verbose output + -l, --list - List the current status of active (or all with -v) monitors + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder Options for use with devices: - -d, --device [device_path] - Get the current video device settings for [device_path] or all devices - -V, --version - Set the Video 4 Linux API version to use for the query, use 1 or 2 - -q, --query - Query the current settings for the device + -d, --device [device_path] - Get the current video device settings for [device_path] or all devices + -V, --version - Set the Video 4 Linux API version to use for the query, use 1 or 2 + -q, --query - Query the current settings for the device Options for use with monitors: - -m, --monitor - Specify which monitor to address, default 1 if absent - -q, --query - Query the current settings for the monitor - -s, --state - Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm, - 3 = alert, 4 = tape - -B, --brightness [value] - Output the current brightness, set to value if given - -C, --contrast [value] - Output the current contrast, set to value if given - -H, --hue [value] - Output the current hue, set to value if given - -O, --colour [value] - Output the current colour, set to value if given - -i, --image [image_index] - Write captured image to disk as .jpg, last image captured - or specified ring buffer index if given. - -S, --scale - With --image specify any scaling (in %%) to be applied to the image - -t, --timestamp [image_index] - Output captured image timestamp, last image captured or specified - ring buffer index if given - -R, --read_index - Output ring buffer read index - -W, --write_index - Output ring buffer write index - -e, --event - Output last event index - -f, --fps - Output last Frames Per Second captured reading - -z, --zones - Write last captured image overlaid with zones to -Zones.jpg - -a, --alarm - Force alarm in monitor, this will trigger recording until cancelled with -c - -n, --noalarm - Force no alarms in monitor, this will prevent alarms until cancelled with -c - -c, --cancel - Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n - -L, --reload - Signal monitor to reload settings - -E, --enable - Enable detection, wake monitor up - -D, --disable - Disable detection, put monitor to sleep - -u, --suspend - Suspend detection, useful to prevent bogus alarms when panning etc - -r, --resume - Resume detection after a suspend - -U, --username - When running in authenticated mode the username and - -P, --password - password combination of the given user - -A, --auth - Pass authentication hash string instead of user details + -m, --monitor - Specify which monitor to address, default 1 if absent + -q, --query - Query the current settings for the monitor + -s, --state - Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm, + 3 = alert, 4 = tape + -B, --brightness [value] - Output the current brightness, set to value if given + -C, --contrast [value] - Output the current contrast, set to value if given + -H, --hue [value] - Output the current hue, set to value if given + -O, --colour [value] - Output the current colour, set to value if given + -i, --image [image_index] - Write captured image to disk as .jpg, last image captured + or specified ring buffer index if given. + -S, --scale - With --image specify any scaling (in %%) to be applied to the image + -t, --timestamp [image_index] - Output captured image timestamp, last image captured or specified + ring buffer index if given + -R, --read_index - Output ring buffer read index + -W, --write_index - Output ring buffer write index + -e, --event - Output last event index + -f, --fps - Output last Frames Per Second captured reading + -z, --zones - Write last captured image overlaid with zones to -Zones.jpg + -a, --alarm - Force alarm in monitor, this will trigger recording until cancelled with -c + -n, --noalarm - Force no alarms in monitor, this will prevent alarms until cancelled with -c + -c, --cancel - Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n + -L, --reload - Signal monitor to reload settings + -E, --enable - Enable detection, wake monitor up + -D, --disable - Disable detection, put monitor to sleep + -u, --suspend - Suspend detection, useful to prevent bogus alarms when panning etc + -r, --resume - Resume detection after a suspend + -U, --username - When running in authenticated mode the username and + -P, --password - password combination of the given user + -A, --auth - Pass authentication hash string instead of user details =cut @@ -97,739 +97,738 @@ Options for use with monitors: void Usage( int status=-1 ) { - fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U -P]\n" ); - fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U -P]\n" ); - fprintf( stderr, "General options:\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); - fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); - fprintf( stderr, "Options for use with devices:\n" ); - fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); - fprintf( stderr, " -V, --version : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); - fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); - fprintf( stderr, "Options for use with monitors:\n" ); - fprintf( stderr, " -m, --monitor : Specify which monitor to address, default 1 if absent\n" ); - fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); - fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); - fprintf( stderr, " 3 = alert, 4 = tape\n" ); - fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); - fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); - fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); - fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); - fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as .jpg, last image captured\n" ); - fprintf( stderr, " or specified ring buffer index if given.\n" ); - fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); - fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); - fprintf( stderr, " ring buffer index if given\n" ); - fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); - fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); - fprintf( stderr, " -e, --event : Output last event index\n" ); - fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); - fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); - fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); - fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); - fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); - fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); - fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); - fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); - fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); - fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); - fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); - fprintf( stderr, " -P, --password : password combination of the given user\n" ); - fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); + fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U -P]\n" ); + fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U -P]\n" ); + fprintf( stderr, "General options:\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); + fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); + fprintf( stderr, "Options for use with devices:\n" ); + fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); + fprintf( stderr, " -V, --version : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); + fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); + fprintf( stderr, "Options for use with monitors:\n" ); + fprintf( stderr, " -m, --monitor : Specify which monitor to address, default 1 if absent\n" ); + fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); + fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); + fprintf( stderr, " 3 = alert, 4 = tape\n" ); + fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); + fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); + fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); + fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); + fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as .jpg, last image captured\n" ); + fprintf( stderr, " or specified ring buffer index if given.\n" ); + fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); + fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); + fprintf( stderr, " ring buffer index if given\n" ); + fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); + fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); + fprintf( stderr, " -e, --event : Output last event index\n" ); + fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); + fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); + fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); + fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); + fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); + fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); + fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); + fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); + fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); + fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); + fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); + fprintf( stderr, " -P, --password : password combination of the given user\n" ); + fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); - exit( status ); + exit( status ); } typedef enum { - ZMU_BOGUS = 0x00000000, - ZMU_STATE = 0x00000001, - ZMU_IMAGE = 0x00000002, - ZMU_TIME = 0x00000004, - ZMU_READ_IDX = 0x00000008, - ZMU_WRITE_IDX = 0x00000010, - ZMU_EVENT = 0x00000020, - ZMU_FPS = 0x00000040, - ZMU_ZONES = 0x00000080, - ZMU_ALARM = 0x00000100, - ZMU_NOALARM = 0x00000200, - ZMU_CANCEL = 0x00000400, - ZMU_QUERY = 0x00000800, - ZMU_BRIGHTNESS = 0x00001000, - ZMU_CONTRAST = 0x00002000, - ZMU_HUE = 0x00004000, - ZMU_COLOUR = 0x00008000, - ZMU_RELOAD = 0x00010000, - ZMU_ENABLE = 0x00100000, - ZMU_DISABLE = 0x00200000, - ZMU_SUSPEND = 0x00400000, - ZMU_RESUME = 0x00800000, - ZMU_LIST = 0x10000000, + ZMU_BOGUS = 0x00000000, + ZMU_STATE = 0x00000001, + ZMU_IMAGE = 0x00000002, + ZMU_TIME = 0x00000004, + ZMU_READ_IDX = 0x00000008, + ZMU_WRITE_IDX = 0x00000010, + ZMU_EVENT = 0x00000020, + ZMU_FPS = 0x00000040, + ZMU_ZONES = 0x00000080, + ZMU_ALARM = 0x00000100, + ZMU_NOALARM = 0x00000200, + ZMU_CANCEL = 0x00000400, + ZMU_QUERY = 0x00000800, + ZMU_BRIGHTNESS = 0x00001000, + ZMU_CONTRAST = 0x00002000, + ZMU_HUE = 0x00004000, + ZMU_COLOUR = 0x00008000, + ZMU_RELOAD = 0x00010000, + ZMU_ENABLE = 0x00100000, + ZMU_DISABLE = 0x00200000, + ZMU_SUSPEND = 0x00400000, + ZMU_RESUME = 0x00800000, + ZMU_LIST = 0x10000000, } Function; bool ValidateAccess( User *user, int mon_id, int function ) { - bool allowed = true; - if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) - { - if ( user->getStream() < User::PERM_VIEW ) - allowed = false; - } - if ( function & ZMU_EVENT ) - { - if ( user->getEvents() < User::PERM_VIEW ) - allowed = false; - } - if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) - { - if ( user->getMonitors() < User::PERM_VIEW ) - allowed = false; - } - if ( function & (ZMU_ALARM|ZMU_NOALARM|ZMU_CANCEL|ZMU_RELOAD|ZMU_ENABLE|ZMU_DISABLE|ZMU_SUSPEND|ZMU_RESUME|ZMU_BRIGHTNESS|ZMU_CONTRAST|ZMU_HUE|ZMU_COLOUR) ) - { - if ( user->getMonitors() < User::PERM_EDIT ) - allowed = false; - } - if ( mon_id > 0 ) - { - if ( !user->canAccess( mon_id ) ) - { - allowed = false; - } - } - if ( !allowed ) - { - fprintf( stderr, "Error, insufficient privileges for requested action\n" ); - exit( -1 ); - } - return( allowed ); + bool allowed = true; + if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) + { + if ( user->getStream() < User::PERM_VIEW ) + allowed = false; + } + if ( function & ZMU_EVENT ) + { + if ( user->getEvents() < User::PERM_VIEW ) + allowed = false; + } + if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) + { + if ( user->getMonitors() < User::PERM_VIEW ) + allowed = false; + } + if ( function & (ZMU_ALARM|ZMU_NOALARM|ZMU_CANCEL|ZMU_RELOAD|ZMU_ENABLE|ZMU_DISABLE|ZMU_SUSPEND|ZMU_RESUME|ZMU_BRIGHTNESS|ZMU_CONTRAST|ZMU_HUE|ZMU_COLOUR) ) + { + if ( user->getMonitors() < User::PERM_EDIT ) + allowed = false; + } + if ( mon_id > 0 ) + { + if ( !user->canAccess( mon_id ) ) + { + allowed = false; + } + } + if ( !allowed ) + { + fprintf( stderr, "Error, insufficient privileges for requested action\n" ); + exit( -1 ); + } + return( allowed ); } int main( int argc, char *argv[] ) { - if ( access(ZM_CONFIG, R_OK) != 0 ) - { - fprintf( stderr, "Can't open %s: %s\n", ZM_CONFIG, strerror(errno) ); - exit( -1 ); - } + if ( access(ZM_CONFIG, R_OK) != 0 ) + { + fprintf( stderr, "Can't open %s: %s\n", ZM_CONFIG, strerror(errno) ); + exit( -1 ); + } - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - static struct option long_options[] = { - {"device", 2, 0, 'd'}, - {"monitor", 1, 0, 'm'}, - {"verbose", 0, 0, 'v'}, - {"image", 2, 0, 'i'}, - {"scale", 1, 0, 'S'}, - {"timestamp", 2, 0, 't'}, - {"state", 0, 0, 's'}, - {"brightness", 2, 0, 'B'}, - {"contrast", 2, 0, 'C'}, - {"hue", 2, 0, 'H'}, - {"contrast", 2, 0, 'O'}, - {"read_index", 0, 0, 'R'}, - {"write_index", 0, 0, 'W'}, - {"event", 0, 0, 'e'}, - {"fps", 0, 0, 'f'}, - {"zones", 2, 0, 'z'}, - {"alarm", 0, 0, 'a'}, - {"noalarm", 0, 0, 'n'}, - {"cancel", 0, 0, 'c'}, - {"reload", 0, 0, 'L'}, - {"enable", 0, 0, 'E'}, - {"disable", 0, 0, 'D'}, - {"suspend", 0, 0, 'u'}, - {"resume", 0, 0, 'r'}, - {"query", 0, 0, 'q'}, - {"username", 1, 0, 'U'}, - {"password", 1, 0, 'P'}, - {"auth", 1, 0, 'A'}, - {"version", 1, 0, 'V'}, - {"help", 0, 0, 'h'}, - {"list", 0, 0, 'l'}, - {0, 0, 0, 0} - }; + static struct option long_options[] = { + {"device", 2, 0, 'd'}, + {"monitor", 1, 0, 'm'}, + {"verbose", 0, 0, 'v'}, + {"image", 2, 0, 'i'}, + {"scale", 1, 0, 'S'}, + {"timestamp", 2, 0, 't'}, + {"state", 0, 0, 's'}, + {"brightness", 2, 0, 'B'}, + {"contrast", 2, 0, 'C'}, + {"hue", 2, 0, 'H'}, + {"contrast", 2, 0, 'O'}, + {"read_index", 0, 0, 'R'}, + {"write_index", 0, 0, 'W'}, + {"event", 0, 0, 'e'}, + {"fps", 0, 0, 'f'}, + {"zones", 2, 0, 'z'}, + {"alarm", 0, 0, 'a'}, + {"noalarm", 0, 0, 'n'}, + {"cancel", 0, 0, 'c'}, + {"reload", 0, 0, 'L'}, + {"enable", 0, 0, 'E'}, + {"disable", 0, 0, 'D'}, + {"suspend", 0, 0, 'u'}, + {"resume", 0, 0, 'r'}, + {"query", 0, 0, 'q'}, + {"username", 1, 0, 'U'}, + {"password", 1, 0, 'P'}, + {"auth", 1, 0, 'A'}, + {"version", 1, 0, 'V'}, + {"help", 0, 0, 'h'}, + {"list", 0, 0, 'l'}, + {0, 0, 0, 0} + }; - const char *device = 0; - int mon_id = 0; - bool verbose = false; - int function = ZMU_BOGUS; + const char *device = 0; + int mon_id = 0; + bool verbose = false; + int function = ZMU_BOGUS; - int image_idx = -1; - int scale = -1; - int brightness = -1; - int contrast = -1; - int hue = -1; - int colour = -1; - char *zoneString = 0; - char *username = 0; - char *password = 0; - char *auth = 0; + int image_idx = -1; + int scale = -1; + int brightness = -1; + int contrast = -1; + int hue = -1; + int colour = -1; + char *zoneString = 0; + char *username = 0; + char *password = 0; + char *auth = 0; #if ZM_HAS_V4L #if ZM_HAS_V4L2 - int v4lVersion = 2; + int v4lVersion = 2; #elif ZM_HAS_V4L1 - int v4lVersion = 1; + int v4lVersion = 1; #endif // ZM_HAS_V4L2/1 #endif // ZM_HAS_V4L - while (1) - { - int option_index = 0; + while (1) + { + int option_index = 0; - int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); - if (c == -1) - { - break; - } + int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); + if (c == -1) + { + break; + } - switch (c) - { - case 'd': - if ( optarg ) - device = optarg; - break; - case 'm': - mon_id = atoi(optarg); - break; - case 'v': - verbose = true; - break; - case 's': - function |= ZMU_STATE; - break; - case 'i': - function |= ZMU_IMAGE; - if ( optarg ) - image_idx = atoi( optarg ); - break; - case 'S': - scale = atoi(optarg); - break; - case 't': - function |= ZMU_TIME; - if ( optarg ) - image_idx = atoi( optarg ); - break; - case 'R': - function |= ZMU_READ_IDX; - break; - case 'W': - function |= ZMU_WRITE_IDX; - break; - case 'e': - function |= ZMU_EVENT; - break; - case 'f': - function |= ZMU_FPS; - break; - case 'z': - function |= ZMU_ZONES; - if ( optarg ) - zoneString = optarg; - break; - case 'a': - function |= ZMU_ALARM; - break; - case 'n': - function |= ZMU_NOALARM; - break; - case 'c': - function |= ZMU_CANCEL; - break; - case 'L': - function |= ZMU_RELOAD; - break; - case 'E': - function |= ZMU_ENABLE; - break; - case 'D': - function |= ZMU_DISABLE; - break; - case 'u': - function |= ZMU_SUSPEND; - break; - case 'r': - function |= ZMU_RESUME; - break; - case 'q': - function |= ZMU_QUERY; - break; - case 'B': - function |= ZMU_BRIGHTNESS; - if ( optarg ) - brightness = atoi( optarg ); - break; - case 'C': - function |= ZMU_CONTRAST; - if ( optarg ) - contrast = atoi( optarg ); - break; - case 'H': - function |= ZMU_HUE; - if ( optarg ) - hue = atoi( optarg ); - break; - case 'O': - function |= ZMU_COLOUR; - if ( optarg ) - colour = atoi( optarg ); - break; - case 'U': - username = optarg; - break; - case 'P': - password = optarg; - break; - case 'A': - auth = optarg; - break; + switch (c) + { + case 'd': + if ( optarg ) + device = optarg; + break; + case 'm': + mon_id = atoi(optarg); + break; + case 'v': + verbose = true; + break; + case 's': + function |= ZMU_STATE; + break; + case 'i': + function |= ZMU_IMAGE; + if ( optarg ) + image_idx = atoi( optarg ); + break; + case 'S': + scale = atoi(optarg); + break; + case 't': + function |= ZMU_TIME; + if ( optarg ) + image_idx = atoi( optarg ); + break; + case 'R': + function |= ZMU_READ_IDX; + break; + case 'W': + function |= ZMU_WRITE_IDX; + break; + case 'e': + function |= ZMU_EVENT; + break; + case 'f': + function |= ZMU_FPS; + break; + case 'z': + function |= ZMU_ZONES; + if ( optarg ) + zoneString = optarg; + break; + case 'a': + function |= ZMU_ALARM; + break; + case 'n': + function |= ZMU_NOALARM; + break; + case 'c': + function |= ZMU_CANCEL; + break; + case 'L': + function |= ZMU_RELOAD; + break; + case 'E': + function |= ZMU_ENABLE; + break; + case 'D': + function |= ZMU_DISABLE; + break; + case 'u': + function |= ZMU_SUSPEND; + break; + case 'r': + function |= ZMU_RESUME; + break; + case 'q': + function |= ZMU_QUERY; + break; + case 'B': + function |= ZMU_BRIGHTNESS; + if ( optarg ) + brightness = atoi( optarg ); + break; + case 'C': + function |= ZMU_CONTRAST; + if ( optarg ) + contrast = atoi( optarg ); + break; + case 'H': + function |= ZMU_HUE; + if ( optarg ) + hue = atoi( optarg ); + break; + case 'O': + function |= ZMU_COLOUR; + if ( optarg ) + colour = atoi( optarg ); + break; + case 'U': + username = optarg; + break; + case 'P': + password = optarg; + break; + case 'A': + auth = optarg; + break; #if ZM_HAS_V4L - case 'V': - v4lVersion = (atoi(optarg)==1)?1:2; - break; + case 'V': + v4lVersion = (atoi(optarg)==1)?1:2; + break; #endif // ZM_HAS_V4L - case 'h': - Usage( 0 ); - break; - case 'l': - function |= ZMU_LIST; - break; - case '?': - Usage(); - break; - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } + case 'h': + Usage( 0 ); + break; + case 'l': + function |= ZMU_LIST; + break; + case '?': + Usage(); + break; + default: + //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); + break; + } + } - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - fprintf( stderr, "%s ", argv[optind++]); - fprintf( stderr, "\n"); - Usage(); - } + if (optind < argc) + { + fprintf( stderr, "Extraneous options, " ); + while (optind < argc) + fprintf( stderr, "%s ", argv[optind++]); + fprintf( stderr, "\n"); + Usage(); + } - if ( device && !(function&ZMU_QUERY) ) - { - fprintf( stderr, "Error, -d option cannot be used with this option\n" ); - Usage(); - } - if ( scale != -1 && !(function&ZMU_IMAGE) ) - { - fprintf( stderr, "Error, -S option cannot be used with this option\n" ); - Usage(); - } - //printf( "Monitor %d, Function %d\n", mon_id, function ); + if ( device && !(function&ZMU_QUERY) ) + { + fprintf( stderr, "Error, -d option cannot be used with this option\n" ); + Usage(); + } + if ( scale != -1 && !(function&ZMU_IMAGE) ) + { + fprintf( stderr, "Error, -S option cannot be used with this option\n" ); + Usage(); + } + //printf( "Monitor %d, Function %d\n", mon_id, function ); - zmLoadConfig(); + zmLoadConfig(); - logInit( "zmu" ); + logInit( "zmu" ); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - User *user = 0; + User *user = 0; - if ( config.opt_use_auth ) - { - if ( strcmp( config.auth_relay, "none" ) == 0 ) - { - if ( !username ) - { - fprintf( stderr, "Error, username must be supplied\n" ); - exit( -1 ); - } + if ( config.opt_use_auth ) + { + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( !username ) + { + fprintf( stderr, "Error, username must be supplied\n" ); + exit( -1 ); + } - if ( username ) - { - user = zmLoadUser( username ); - } - } - else - { - if ( !(username && password) && !auth ) - { - fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); - exit( -1 ); - } + if ( username ) + { + user = zmLoadUser( username ); + } + } + else + { + if ( !(username && password) && !auth ) + { + fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); + exit( -1 ); + } - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( auth ) - { - user = zmLoadAuthUser( auth, false ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( username && password ) - { - user = zmLoadUser( username, password ); - } - } - } - if ( !user ) - { - fprintf( stderr, "Error, unable to authenticate user\n" ); - exit( -1 ); - } - ValidateAccess( user, mon_id, function ); - } - + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( auth ) + { + user = zmLoadAuthUser( auth, false ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( username && password ) + { + user = zmLoadUser( username, password ); + } + } + } + if ( !user ) + { + fprintf( stderr, "Error, unable to authenticate user\n" ); + exit( -1 ); + } + ValidateAccess( user, mon_id, function ); + } + - if ( mon_id > 0 ) - { - Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); - if ( monitor ) - { - if ( verbose ) - { - printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); - } - if ( ! monitor->connect() ) { - Error( "Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name() ); - exit( -1 ); - } + if ( mon_id > 0 ) + { + Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); + if ( monitor ) + { + if ( verbose ) + { + printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); + } + if ( ! monitor->connect() ) { + Error( "Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name() ); + } - char separator = ' '; - bool have_output = false; - if ( function & ZMU_STATE ) - { - Monitor::State state = monitor->GetState(); - if ( verbose ) - printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", state ); - have_output = true; - } - } - if ( function & ZMU_TIME ) - { - struct timeval timestamp = monitor->GetTimestamp( image_idx ); - if ( verbose ) - { - char timestamp_str[64] = "None"; - if ( timestamp.tv_sec ) - strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) ); - if ( image_idx == -1 ) - printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 ); - else - printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 ); - } - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 ); - have_output = true; - } - } - if ( function & ZMU_READ_IDX ) - { - if ( verbose ) - printf( "Last read index: %d\n", monitor->GetLastReadIndex() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastReadIndex() ); - have_output = true; - } - } - if ( function & ZMU_WRITE_IDX ) - { - if ( verbose ) - printf( "Last write index: %d\n", monitor->GetLastWriteIndex() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastWriteIndex() ); - have_output = true; - } - } - if ( function & ZMU_EVENT ) - { - if ( verbose ) - printf( "Last event id: %d\n", monitor->GetLastEvent() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastEvent() ); - have_output = true; - } - } - if ( function & ZMU_FPS ) - { - if ( verbose ) - printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%.2f", monitor->GetFPS() ); - have_output = true; - } - } - if ( function & ZMU_IMAGE ) - { - if ( verbose ) - { - if ( image_idx == -1 ) - printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() ); - else - printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() ); - if ( scale != -1 ) - printf( ", scaling by %d%%", scale ); - printf( "\n" ); - } - monitor->GetImage( image_idx, scale>0?scale:100 ); - } - if ( function & ZMU_ZONES ) - { - if ( verbose ) - printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() ); - monitor->DumpZoneImage( zoneString ); - } - if ( function & ZMU_ALARM ) - { - if ( verbose ) - printf( "Forcing alarm on\n" ); - monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" ); - } - if ( function & ZMU_NOALARM ) - { - if ( verbose ) - printf( "Forcing alarm off\n" ); - monitor->ForceAlarmOff(); - } - if ( function & ZMU_CANCEL ) - { - if ( verbose ) - printf( "Cancelling forced alarm on/off\n" ); - monitor->CancelForced(); - } - if ( function & ZMU_RELOAD ) - { - if ( verbose ) - printf( "Reloading monitor settings\n" ); - monitor->actionReload(); - } - if ( function & ZMU_ENABLE ) - { - if ( verbose ) - printf( "Enabling event generation\n" ); - monitor->actionEnable(); - } - if ( function & ZMU_DISABLE ) - { - if ( verbose ) - printf( "Disabling event generation\n" ); - monitor->actionDisable(); - } - if ( function & ZMU_SUSPEND ) - { - if ( verbose ) - printf( "Suspending event generation\n" ); - monitor->actionSuspend(); - } - if ( function & ZMU_RESUME ) - { - if ( verbose ) - printf( "Resuming event generation\n" ); - monitor->actionResume(); - } - if ( function & ZMU_QUERY ) - { - char monString[16382] = ""; - monitor->DumpSettings( monString, verbose ); - printf( "%s\n", monString ); - } - if ( function & ZMU_BRIGHTNESS ) - { - if ( verbose ) - { - if ( brightness >= 0 ) - printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) ); - else - printf( "Current brightness: %d\n", monitor->actionBrightness() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( brightness >= 0 ) - printf( "%d", monitor->actionBrightness( brightness ) ); - else - printf( "%d", monitor->actionBrightness() ); - have_output = true; - } - } - if ( function & ZMU_CONTRAST ) - { - if ( verbose ) - { - if ( contrast >= 0 ) - printf( "New brightness: %d\n", monitor->actionContrast( contrast ) ); - else - printf( "Current contrast: %d\n", monitor->actionContrast() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( contrast >= 0 ) - printf( "%d", monitor->actionContrast( contrast ) ); - else - printf( "%d", monitor->actionContrast() ); - have_output = true; - } - } - if ( function & ZMU_HUE ) - { - if ( verbose ) - { - if ( hue >= 0 ) - printf( "New hue: %d\n", monitor->actionHue( hue ) ); - else - printf( "Current hue: %d\n", monitor->actionHue() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( hue >= 0 ) - printf( "%d", monitor->actionHue( hue ) ); - else - printf( "%d", monitor->actionHue() ); - have_output = true; - } - } - if ( function & ZMU_COLOUR ) - { - if ( verbose ) - { - if ( colour >= 0 ) - printf( "New colour: %d\n", monitor->actionColour( colour ) ); - else - printf( "Current colour: %d\n", monitor->actionColour() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( colour >= 0 ) - printf( "%d", monitor->actionColour( colour ) ); - else - printf( "%d", monitor->actionColour() ); - have_output = true; - } - } - if ( have_output ) - { - printf( "\n" ); - } - if ( !function ) - { - Usage(); - } - delete monitor; - } - else - { - fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); - exit( -1 ); - } - } - else - { - if ( function & ZMU_QUERY ) - { + char separator = ' '; + bool have_output = false; + if ( function & ZMU_STATE ) + { + Monitor::State state = monitor->GetState(); + if ( verbose ) + printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", state ); + have_output = true; + } + } + if ( function & ZMU_TIME ) + { + struct timeval timestamp = monitor->GetTimestamp( image_idx ); + if ( verbose ) + { + char timestamp_str[64] = "None"; + if ( timestamp.tv_sec ) + strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) ); + if ( image_idx == -1 ) + printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 ); + else + printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 ); + } + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 ); + have_output = true; + } + } + if ( function & ZMU_READ_IDX ) + { + if ( verbose ) + printf( "Last read index: %d\n", monitor->GetLastReadIndex() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastReadIndex() ); + have_output = true; + } + } + if ( function & ZMU_WRITE_IDX ) + { + if ( verbose ) + printf( "Last write index: %d\n", monitor->GetLastWriteIndex() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastWriteIndex() ); + have_output = true; + } + } + if ( function & ZMU_EVENT ) + { + if ( verbose ) + printf( "Last event id: %d\n", monitor->GetLastEvent() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastEvent() ); + have_output = true; + } + } + if ( function & ZMU_FPS ) + { + if ( verbose ) + printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%.2f", monitor->GetFPS() ); + have_output = true; + } + } + if ( function & ZMU_IMAGE ) + { + if ( verbose ) + { + if ( image_idx == -1 ) + printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() ); + else + printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() ); + if ( scale != -1 ) + printf( ", scaling by %d%%", scale ); + printf( "\n" ); + } + monitor->GetImage( image_idx, scale>0?scale:100 ); + } + if ( function & ZMU_ZONES ) + { + if ( verbose ) + printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() ); + monitor->DumpZoneImage( zoneString ); + } + if ( function & ZMU_ALARM ) + { + if ( verbose ) + printf( "Forcing alarm on\n" ); + monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" ); + } + if ( function & ZMU_NOALARM ) + { + if ( verbose ) + printf( "Forcing alarm off\n" ); + monitor->ForceAlarmOff(); + } + if ( function & ZMU_CANCEL ) + { + if ( verbose ) + printf( "Cancelling forced alarm on/off\n" ); + monitor->CancelForced(); + } + if ( function & ZMU_RELOAD ) + { + if ( verbose ) + printf( "Reloading monitor settings\n" ); + monitor->actionReload(); + } + if ( function & ZMU_ENABLE ) + { + if ( verbose ) + printf( "Enabling event generation\n" ); + monitor->actionEnable(); + } + if ( function & ZMU_DISABLE ) + { + if ( verbose ) + printf( "Disabling event generation\n" ); + monitor->actionDisable(); + } + if ( function & ZMU_SUSPEND ) + { + if ( verbose ) + printf( "Suspending event generation\n" ); + monitor->actionSuspend(); + } + if ( function & ZMU_RESUME ) + { + if ( verbose ) + printf( "Resuming event generation\n" ); + monitor->actionResume(); + } + if ( function & ZMU_QUERY ) + { + char monString[16382] = ""; + monitor->DumpSettings( monString, verbose ); + printf( "%s\n", monString ); + } + if ( function & ZMU_BRIGHTNESS ) + { + if ( verbose ) + { + if ( brightness >= 0 ) + printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) ); + else + printf( "Current brightness: %d\n", monitor->actionBrightness() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( brightness >= 0 ) + printf( "%d", monitor->actionBrightness( brightness ) ); + else + printf( "%d", monitor->actionBrightness() ); + have_output = true; + } + } + if ( function & ZMU_CONTRAST ) + { + if ( verbose ) + { + if ( contrast >= 0 ) + printf( "New brightness: %d\n", monitor->actionContrast( contrast ) ); + else + printf( "Current contrast: %d\n", monitor->actionContrast() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( contrast >= 0 ) + printf( "%d", monitor->actionContrast( contrast ) ); + else + printf( "%d", monitor->actionContrast() ); + have_output = true; + } + } + if ( function & ZMU_HUE ) + { + if ( verbose ) + { + if ( hue >= 0 ) + printf( "New hue: %d\n", monitor->actionHue( hue ) ); + else + printf( "Current hue: %d\n", monitor->actionHue() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( hue >= 0 ) + printf( "%d", monitor->actionHue( hue ) ); + else + printf( "%d", monitor->actionHue() ); + have_output = true; + } + } + if ( function & ZMU_COLOUR ) + { + if ( verbose ) + { + if ( colour >= 0 ) + printf( "New colour: %d\n", monitor->actionColour( colour ) ); + else + printf( "Current colour: %d\n", monitor->actionColour() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( colour >= 0 ) + printf( "%d", monitor->actionColour( colour ) ); + else + printf( "%d", monitor->actionColour() ); + have_output = true; + } + } + if ( have_output ) + { + printf( "\n" ); + } + if ( !function ) + { + Usage(); + } + delete monitor; + } + else + { + fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); + exit( -1 ); + } + } + else + { + if ( function & ZMU_QUERY ) + { #if ZM_HAS_V4L - char vidString[0x10000] = ""; - bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); - printf( "%s", vidString ); - exit( ok?0:-1 ); + char vidString[0x10000] = ""; + bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); + printf( "%s", vidString ); + exit( ok?0:-1 ); #else // ZM_HAS_V4L - fprintf( stderr, "Error, video4linux is required for device querying\n" ); - exit( -1 ); + fprintf( stderr, "Error, video4linux is required for device querying\n" ); + exit( -1 ); #endif // ZM_HAS_V4L - } + } - if ( function & ZMU_LIST ) - { - std::string sql = "select Id, Function+0 from Monitors"; - if ( !verbose ) - { - sql += "where Function != 'None'"; - } - sql += " order by Id asc"; + if ( function & ZMU_LIST ) + { + std::string sql = "select Id, Function+0 from Monitors"; + if ( !verbose ) + { + sql += "where Function != 'None'"; + } + sql += " order by Id asc"; - if ( mysql_query( &dbconn, sql.c_str() ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql.c_str() ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); - printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" ); - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int mon_id = atoi(dbrow[0]); - int function = atoi(dbrow[1]); - if ( !user || user->canAccess( mon_id ) ) - { - if ( function > 1 ) - { - Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); - if ( monitor && monitor->connect() ) - { - struct timeval tv = monitor->GetTimestamp(); - printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", - monitor->Id(), - function, - monitor->GetState(), - monitor->GetTriggerState(), - tv.tv_sec, tv.tv_usec/10000, - monitor->GetLastReadIndex(), - monitor->GetLastWriteIndex(), - monitor->GetLastEvent(), - monitor->GetFPS() - ); - delete monitor; - } - } - else - { - struct timeval tv = { 0, 0 }; - printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", - mon_id, - function, - 0, - 0, - tv.tv_sec, tv.tv_usec/10000, - 0, - 0, - 0, - 0.0 - ); - } - } - } - mysql_free_result( result ); - } - } - delete user; + printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" ); + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int mon_id = atoi(dbrow[0]); + int function = atoi(dbrow[1]); + if ( !user || user->canAccess( mon_id ) ) + { + if ( function > 1 ) + { + Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); + if ( monitor && monitor->connect() ) + { + struct timeval tv = monitor->GetTimestamp(); + printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", + monitor->Id(), + function, + monitor->GetState(), + monitor->GetTriggerState(), + tv.tv_sec, tv.tv_usec/10000, + monitor->GetLastReadIndex(), + monitor->GetLastWriteIndex(), + monitor->GetLastEvent(), + monitor->GetFPS() + ); + delete monitor; + } + } + else + { + struct timeval tv = { 0, 0 }; + printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", + mon_id, + function, + 0, + 0, + tv.tv_sec, tv.tv_usec/10000, + 0, + 0, + 0, + 0.0 + ); + } + } + } + mysql_free_result( result ); + } + } + delete user; - logTerm(); - zmDbClose(); + logTerm(); + zmDbClose(); - return( 0 ); + return( 0 ); } diff --git a/web/.editorconfig b/web/.editorconfig new file mode 100644 index 000000000..217a0e30c --- /dev/null +++ b/web/.editorconfig @@ -0,0 +1,13 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +indent_style = tab +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.bat] +end_of_line = crlf diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 000000000..90d971d4b --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,22 @@ +# User specific & automatically generated files # +################################################# +/app/Config/database.php +/app/tmp +/lib/Cake/Console/Templates/skel/tmp/ +/plugins +/vendors +/build +/dist +/tags +/app/webroot/events + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db diff --git a/web/.htaccess b/web/.htaccess new file mode 100644 index 000000000..f23dbaf66 --- /dev/null +++ b/web/.htaccess @@ -0,0 +1,5 @@ + + RewriteEngine on + RewriteRule ^$ app/webroot/ [L] + RewriteRule (.*) app/webroot/$1 [L] + \ No newline at end of file diff --git a/web/.travis.yml b/web/.travis.yml new file mode 100644 index 000000000..5283442c4 --- /dev/null +++ b/web/.travis.yml @@ -0,0 +1,116 @@ +language: php + +php: + - 5.2 + - 5.3 + - 5.4 + +env: + - DB=mysql + - DB=pgsql + - DB=sqlite + +matrix: + include: + - php: 5.4 + env: + - PHPCS=1 + +before_script: + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test2;'; fi" + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test3;'; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE SCHEMA test2;' -U postgres -d cakephp_test; fi" + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE SCHEMA test3;' -U postgres -d cakephp_test; fi" + - chmod -R 777 ./app/tmp + - sudo apt-get install lighttpd + - pear channel-discover pear.cakephp.org + - pear install --alldeps cakephp/CakePHP_CodeSniffer + - phpenv rehash + - set +H + - echo " array( + 'datasource' => 'Database/Mysql', + 'host' => '0.0.0.0', + 'login' => 'travis' + ), + 'pgsql' => array( + 'datasource' => 'Database/Postgres', + 'host' => '127.0.0.1', + 'login' => 'postgres', + 'database' => 'cakephp_test', + 'schema' => array( + 'default' => 'public', + 'test' => 'public', + 'test2' => 'test2', + 'test_database_three' => 'test3' + ) + ), + 'sqlite' => array( + 'datasource' => 'Database/Sqlite', + 'database' => array( + 'default' => ':memory:', + 'test' => ':memory:', + 'test2' => '/tmp/cakephp_test2.db', + 'test_database_three' => '/tmp/cakephp_test3.db' + ), + ) + ); + public \$default = array( + 'persistent' => false, + 'host' => '', + 'login' => '', + 'password' => '', + 'database' => 'cakephp_test', + 'prefix' => '' + ); + public \$test = array( + 'persistent' => false, + 'host' => '', + 'login' => '', + 'password' => '', + 'database' => 'cakephp_test', + 'prefix' => '' + ); + public \$test2 = array( + 'persistent' => false, + 'host' => '', + 'login' => '', + 'password' => '', + 'database' => 'cakephp_test2', + 'prefix' => '' + ); + public \$test_database_three = array( + 'persistent' => false, + 'host' => '', + 'login' => '', + 'password' => '', + 'database' => 'cakephp_test3', + 'prefix' => '' + ); + public function __construct() { + \$db = 'mysql'; + if (!empty(\$_SERVER['DB'])) { + \$db = \$_SERVER['DB']; + } + foreach (array('default', 'test', 'test2', 'test_database_three') as \$source) { + \$config = array_merge(\$this->{\$source}, \$this->identities[\$db]); + if (is_array(\$config['database'])) { + \$config['database'] = \$config['database'][\$source]; + } + if (!empty(\$config['schema']) && is_array(\$config['schema'])) { + \$config['schema'] = \$config['schema'][\$source]; + } + \$this->{\$source} = \$config; + } + } + }" > app/Config/database.php + +script: + - sh -c "if [ '$PHPCS' != '1' ]; then ./lib/Cake/Console/cake test core AllTests --stderr; else phpcs -p --extensions=php --standard=CakePHP ./lib/Cake; fi" + +notifications: + email: false \ No newline at end of file diff --git a/web/ajax/status.php b/web/ajax/status.php index f7ed3d099..d12e3c9f6 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -117,6 +117,7 @@ $statusData = array( "Height" => true, "Length" => true, "Frames" => true, + "DefaultVideo" => true, "AlarmFrames" => true, "TotScore" => true, "AvgScore" => true, @@ -385,31 +386,33 @@ function getNearEvents() else $midSql = ''; - $sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc'); + $sql = "select E.* as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc'); $result = dbQuery( $sql ); while ( $id = dbFetchNext( $result, 'Id' ) ) { if ( $id == $eventId ) { - $prevId = dbFetchNext( $result, 'Id' ); + $prevEvent = dbFetchNext( $result ); break; } } - $sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; + $sql = "select E.* as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; $result = dbQuery( $sql ); while ( $id = dbFetchNext( $result, 'Id' ) ) { if ( $id == $eventId ) { - $nextId = dbFetchNext( $result, 'Id' ); + $nextEvent = dbFetchNext( $result ); break; } } $result = array( 'EventId'=>$eventId ); - $result['PrevEventId'] = empty($prevId)?0:$prevId; - $result['NextEventId'] = empty($nextId)?0:$nextId; + $result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id']; + $result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id']; + $result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent)); + $result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent)); return( $result ); } diff --git a/web/api/app/Config/bootstrap.php.in b/web/api/app/Config/bootstrap.php.in index cff9a29dd..4e99cf323 100644 --- a/web/api/app/Config/bootstrap.php.in +++ b/web/api/app/Config/bootstrap.php.in @@ -134,8 +134,10 @@ function loadConfigFile() { 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 )) { Configure::write("$matches[1]", "$matches[2]"); + define( $matches[1], $matches[2] ); + } } fclose( $cfg ); } diff --git a/web/api/app/Config/database.php.default b/web/api/app/Config/database.php.default index 184cf7280..fbd4799a1 100644 --- a/web/api/app/Config/database.php.default +++ b/web/api/app/Config/database.php.default @@ -67,10 +67,10 @@ class DATABASE_CONFIG { public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, - 'host' => '@ZM_DB_HOST@', - 'login' => '@ZM_DB_USER@', - 'password' => '@ZM_DB_PASS@', - 'database' => '@ZM_DB_NAME@', + 'host' => ZM_DB_HOST, + 'login' => ZM_DB_USER, + 'password' => ZM_DB_PASS, + 'database' => ZM_DB_NAME, 'prefix' => '', //'encoding' => 'utf8', ); diff --git a/web/api/app/Console/cake.bat b/web/api/app/Console/cake.bat index 919ecac49..c33bf22f8 100644 --- a/web/api/app/Console/cake.bat +++ b/web/api/app/Console/cake.bat @@ -1,31 +1,31 @@ -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Bake is a shell script for running CakePHP bake script -:: -:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) -:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: -:: Licensed under The MIT License -:: Redistributions of files must retain the above copyright notice. -:: -:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: @link http://cakephp.org CakePHP(tm) Project -:: @package app.Console -:: @since CakePHP(tm) v 2.0 -:: @license http://www.opensource.org/licenses/mit-license.php MIT License -:: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -:: In order for this script to work as intended, the cake\console\ folder must be in your PATH - -@echo. -@echo off - -SET app=%0 -SET lib=%~dp0 - -php -q "%lib%cake.php" -working "%CD% " %* - -echo. - -exit /B %ERRORLEVEL% +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: +:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: @link http://cakephp.org CakePHP(tm) Project +:: @package app.Console +:: @since CakePHP(tm) v 2.0 +:: @license http://www.opensource.org/licenses/mit-license.php MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +:: In order for this script to work as intended, the cake\console\ folder must be in your PATH + +@echo. +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD% " %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/web/api/app/Controller/ServersController.php b/web/api/app/Controller/ServersController.php new file mode 100644 index 000000000..b9afe0033 --- /dev/null +++ b/web/api/app/Controller/ServersController.php @@ -0,0 +1,155 @@ +Session->Read('systemPermission'); + if ($canView =='None') { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + +/** + * index method + * + * @return void + */ + public function index() { + $this->Server->recursive = 0; + + $options=''; + $servers = $this->Server->find('all',$options); + $this->set(array( + 'servers' => $servers, + '_serialize' => array('servers') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Server->recursive = 0; + if (!$this->Server->exists($id)) { + throw new NotFoundException(__('Invalid server')); + } + $restricted = ''; + + $options = array('conditions' => array( + array('Server.' . $this->Server->primaryKey => $id), + $restricted + ) + ); + $server = $this->Server->find('first', $options); + $this->set(array( + 'server' => $server, + '_serialize' => array('server') + )); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $this->Server->create(); + if ($this->Server->save($this->request->data)) { + # Might be nice to send it a start request + #$this->daemonControl($this->Server->id, 'start', $this->request->data); + return $this->flash(__('The server has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + $this->Server->id = $id; + + if (!$this->Server->exists($id)) { + throw new NotFoundException(__('Invalid server')); + } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + if ($this->Server->save($this->request->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); + // - restart this server after change + #$this->daemonControl($this->Server->id, 'restart', $this->request->data); + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->Server->id = $id; + if (!$this->Server->exists()) { + throw new NotFoundException(__('Invalid server')); + } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->request->allowMethod('post', 'delete'); + + #$this->daemonControl($this->Server->id, 'stop'); + + if ($this->Server->delete()) { + return $this->flash(__('The server has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The server could not be deleted. Please, try again.'), array('action' => 'index')); + } + } +} diff --git a/web/api/app/Model/Server.php b/web/api/app/Model/Server.php new file mode 100644 index 000000000..b58450db5 --- /dev/null +++ b/web/api/app/Model/Server.php @@ -0,0 +1,74 @@ + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'Monitor' => array( + 'className' => 'Monitor', + 'foreignKey' => 'ServerId', + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ) + ); +} diff --git a/web/api/app/View/Servers/json/edit.ctp b/web/api/app/View/Servers/json/edit.ctp new file mode 100644 index 000000000..0be859571 --- /dev/null +++ b/web/api/app/View/Servers/json/edit.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($server); diff --git a/web/api/app/View/Servers/json/index.ctp b/web/api/app/View/Servers/json/index.ctp new file mode 100644 index 000000000..6e5cbd26d --- /dev/null +++ b/web/api/app/View/Servers/json/index.ctp @@ -0,0 +1 @@ +echo json_encode($servers); diff --git a/web/api/app/View/Servers/json/view.ctp b/web/api/app/View/Servers/json/view.ctp new file mode 100644 index 000000000..c3d0226ab --- /dev/null +++ b/web/api/app/View/Servers/json/view.ctp @@ -0,0 +1 @@ +echo json_encode($server); diff --git a/web/api/app/View/Servers/xml/edit.ctp b/web/api/app/View/Servers/xml/edit.ctp new file mode 100644 index 000000000..09fb8979a --- /dev/null +++ b/web/api/app/View/Servers/xml/edit.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $message)); +echo $xml->asXML(); diff --git a/web/api/app/View/Servers/xml/index.ctp b/web/api/app/View/Servers/xml/index.ctp new file mode 100644 index 000000000..37afc918b --- /dev/null +++ b/web/api/app/View/Servers/xml/index.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitors)); +echo $xml->asXML(); diff --git a/web/api/app/View/Servers/xml/view.ctp b/web/api/app/View/Servers/xml/view.ctp new file mode 100644 index 000000000..b33c6e79a --- /dev/null +++ b/web/api/app/View/Servers/xml/view.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitor)); +echo $xml->asXML(); diff --git a/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat b/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat index 0aa43c024..e37d4a524 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat +++ b/web/api/lib/Cake/Console/Templates/skel/Console/cake.bat @@ -1,30 +1,30 @@ -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Bake is a shell script for running CakePHP bake script -:: -:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) -:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: -:: Licensed under The MIT License -:: Redistributions of files must retain the above copyright notice. -:: -:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: @link http://cakephp.org CakePHP(tm) Project -:: @package app.Console -:: @since CakePHP(tm) v 2.0 -:: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -:: In order for this script to work as intended, the cake\console\ folder must be in your PATH - -@echo. -@echo off - -SET app=%0 -SET lib=%~dp0 - -php -q "%lib%cake.php" -working "%CD% " %* - -echo. - -exit /B %ERRORLEVEL% +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: +:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: @link http://cakephp.org CakePHP(tm) Project +:: @package app.Console +:: @since CakePHP(tm) v 2.0 +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +:: In order for this script to work as intended, the cake\console\ folder must be in your PATH + +@echo. +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD% " %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/web/api/lib/Cake/Console/cake.bat b/web/api/lib/Cake/Console/cake.bat index 905cc39fb..34429cb24 100644 --- a/web/api/lib/Cake/Console/cake.bat +++ b/web/api/lib/Cake/Console/cake.bat @@ -1,28 +1,28 @@ -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: -:: -:: Bake is a shell script for running CakePHP bake script -:: -:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) -:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: -:: Licensed under The MIT License -:: Redistributions of files must retain the above copyright notice. -:: -:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) -:: @link http://cakephp.org CakePHP(tm) Project -:: @package Cake.Console -:: @since CakePHP(tm) v 1.2.0.5012 -:: @license http://www.opensource.org/licenses/mit-license.php MIT License -:: -:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: - -@echo off - -SET app=%0 -SET lib=%~dp0 - -php -q "%lib%cake.php" -working "%CD% " %* - -echo. - -exit /B %ERRORLEVEL% +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:: +:: Bake is a shell script for running CakePHP bake script +:: +:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org) +:: Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: +:: Licensed under The MIT License +:: Redistributions of files must retain the above copyright notice. +:: +:: @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) +:: @link http://cakephp.org CakePHP(tm) Project +:: @package Cake.Console +:: @since CakePHP(tm) v 1.2.0.5012 +:: @license http://www.opensource.org/licenses/mit-license.php MIT License +:: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +@echo off + +SET app=%0 +SET lib=%~dp0 + +php -q "%lib%cake.php" -working "%CD% " %* + +echo. + +exit /B %ERRORLEVEL% diff --git a/web/css/Makefile.am b/web/css/Makefile.am new file mode 100644 index 000000000..3094d2afa --- /dev/null +++ b/web/css/Makefile.am @@ -0,0 +1,9 @@ +AUTOMAKE_OPTIONS = gnu + +webdir = @WEB_PREFIX@/css + +dist_web_DATA = \ + bootstrap.min.css \ + reset.css \ + spinner.css \ + overlay.css diff --git a/web/css/bootstrap.min.css b/web/css/bootstrap.min.css new file mode 100644 index 000000000..4cf729e43 --- /dev/null +++ b/web/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/web/includes/Event.php b/web/includes/Event.php index cd4f831d8..5ad1c4031 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -3,70 +3,150 @@ require_once( 'database.php' ); require_once( 'Storage.php' ); class Event { - public function __construct( $IdOrRow ) { - $row = NULL; - if ( $IdOrRow ) { - if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) ); - if ( ! $row ) { - Error("Unable to load Event record for Id=" . $IdOrRow ); - } - } elseif ( is_array( $IdOrRow ) ) { - $row = $IdOrRow; - } else { - Error("Unknown argument passed to Event Constructor ($IdOrRow)"); - return; - } - } # end if isset($IdOrRow) - - if ( $row ) { - foreach ($row as $k => $v) { - $this->{$k} = $v; - } - } else { - Error("No row for Event " . $IdOrRow ); - } - } // end function __construct - public function Storage() { - return new Storage( $this->{'StorageId'} ); - } - public function __call( $fn, array $args){ - if(isset($this->{$fn})){ - return $this->{$fn}; - #array_unshift($args, $this); - #call_user_func_array( $this->{$fn}, $args); + public function __construct( $IdOrRow ) { + $row = NULL; + if ( $IdOrRow ) { + if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { + $row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( ! $row ) { + Error("Unable to load Event record for Id=" . $IdOrRow ); } + } elseif ( is_array( $IdOrRow ) ) { + $row = $IdOrRow; + } else { + Error("Unknown argument passed to Event Constructor ($IdOrRow)"); + return; + } + } # end if isset($IdOrRow) + + if ( $row ) { + foreach ($row as $k => $v) { + $this->{$k} = $v; + } + } else { + Error("No row for Event " . $IdOrRow ); + } + } // end function __construct + public function Storage() { + return new Storage( $this->{'StorageId'} ); + } + public function __call( $fn, array $args){ + if(isset($this->{$fn})){ + return $this->{$fn}; +#array_unshift($args, $this); +#call_user_func_array( $this->{$fn}, $args); + } + } + + public function Time() { + if ( ! isset( $this->{'Time'} ) ) { + $this->{'Time'} = strtotime($this->{'StartTime'}); + } + return $this->{'Time'}; + } + + public function Path() { + $Storage = $this->Storage(); + return $Storage->Path().'/'.$this->Relative_Path(); + } + public function Relative_Path() { + $event_path = ""; + + if ( ZM_USE_DEEP_STORAGE ) + { + $event_path = + $this->{'MonitorId'} + .'/'.strftime( "%y/%m/%d/%H/%M/%S", + $this->Time() + ) + ; + } + else + { + $event_path = + $this->{'MonitorId'} + .'/'.$this->{'Id'} + ; } - public function Path() { - $Storage = $this->Storage(); - return $Storage->Path().'/'.$this->Relative_Path(); - } - public function Relative_Path() { - $event_path = ""; + return( $event_path ); - if ( ZM_USE_DEEP_STORAGE ) - { - $event_path = - $this->{'MonitorId'} - .'/'.strftime( "%y/%m/%d/%H/%M/%S", - $this->{'Time'} - ) - ; - } - else - { - $event_path = - $this->{'MonitorId'} - .'/'.$this->{'Id'} - ; - } + } - return( $event_path ); + public function LinkPath() { + if ( ZM_USE_DEEP_STORAGE ) { + return $this->{'MonitorId'} .'/'.strftime( "%y/%m/%d/.", $this->Time()).$this->{'Id'}; + } + Error("Calling Link_Path when not using deep storage"); + return ''; + } - } + public function delete() { + dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) ); + if ( !ZM_OPT_FAST_DELETE ) { + dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) ); + dbQuery( 'DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}) ); + if ( ZM_USE_DEEP_STORAGE ) { -} +# Assumption: All events haev a start time + $start_date = date_parse( $this->{'StartTime'} ); + $start_date['year'] = $start_date['year'] % 100; + $Storage = $this->Storage(); +# So this is because ZM creates a link under teh day pointing to the time that the event happened. + $eventlink_path = $Storage->Path().'/'.$this->Link_Path(); + if ( $id_files = glob( $eventlink_path ) ) { +# I know we are using arrays here, but really there can only ever be 1 in the array + $eventPath = preg_replace( '/\.'.$event['Id'].'$/', readlink($id_files[0]), $id_files[0] ); + deletePath( $eventPath ); + deletePath( $id_files[0] ); + $pathParts = explode( '/', $eventPath ); + for ( $i = count($pathParts)-1; $i >= 2; $i-- ) { + $deletePath = join( '/', array_slice( $pathParts, 0, $i ) ); + if ( !glob( $deletePath."/*" ) ) { + deletePath( $deletePath ); + } + } + } else { + Warning( "Found no event files under $eventlink_path" ); + } # end if found files + } else { + $eventPath = $this->Path(); + deletePath( $eventPath ); + } # USE_DEEP_STORAGE OR NOT + } # ! ZM_OPT_FAST_DELETE + } # end Event->delete + +public function getStreamSrc( $args, $querySep='&' ) { + return ZM_BASE_URL.'/index.php?view=view_video&eid='.$this->{'Id'}; + + $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; + + $args[] = "source=event&event=".$this->{'Id'}; + + if ( ZM_OPT_USE_AUTH ) { + if ( ZM_AUTH_RELAY == "hashed" ) { + $args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); + } elseif ( ZM_AUTH_RELAY == "plain" ) { + $args[] = "user=".$_SESSION['username']; + $args[] = "pass=".$_SESSION['password']; + } elseif ( ZM_AUTH_RELAY == "none" ) { + $args[] = "user=".$_SESSION['username']; + } + } + if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) { + $args[] = "connkey=".$GLOBALS['connkey']; + } + if ( ZM_RAND_STREAM ) { + $args[] = "rand=".time(); + } + + if ( count($args) ) { + $streamSrc .= "?".join( $querySep, $args ); + } + + return( $streamSrc ); + } // end function getStreamSrc +} # end class ?> diff --git a/web/includes/Frame.php b/web/includes/Frame.php new file mode 100644 index 000000000..b7add3b0e --- /dev/null +++ b/web/includes/Frame.php @@ -0,0 +1,76 @@ + $v) { + $this->{$k} = $v; + } + } else { + Error("No row for Frame " . $IdOrRow ); + } + } // end function __construct + public function Storage() { + return $this->Event()->Storage(); + } + public function Event() { + return new Event( $this->{'EventId'} ); + } + public function __call( $fn, array $args){ + if(isset($this->{$fn})){ + return $this->{$fn}; +#array_unshift($args, $this); +#call_user_func_array( $this->{$fn}, $args); + } + } + + public function Path() { + $Storage = $this->Storage(); + return $Storage->Path().'/'.$this->Relative_Path(); + } + public function Relative_Path() { + $event_path = ""; + + if ( ZM_USE_DEEP_STORAGE ) + { + $event_path = + $this->{'MonitorId'} + .'/'.strftime( "%y/%m/%d/%H/%M/%S", + $this->Time() + ) + ; + } + else + { + $event_path = + $this->{'MonitorId'} + .'/'.$this->{'Id'} + ; + } + + return( $event_path ); + + } + + public function getImageSrc( ) { + return ZM_BASE_URL.'/index.php?view=image&fid='.$this->{'Id'}; + } // end function getImageSrc +} # end class +?> diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 6b983018e..2b0c9ca65 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -28,8 +28,15 @@ class Monitor { foreach ($s as $k => $v) { if ( $k == 'Id' ) { continue; + } else if ( $k == 'Protocol' ) { + $this->{'ControlProtocol'} = $v; + } else if ( $k == 'Name' ) { + $this->{'ControlName'} = $v; + } else if ( $k == 'Type' ) { + $this->{'ControlType'} = $v; + } else { + $this->{$k} = $v; } - $this->{$k} = $v; } } @@ -79,7 +86,7 @@ class Monitor { } return( $streamSrc ); - } // end function etStreamSrc + } // end function getStreamSrc public function Width() { if ( $this->Orientation() == '90' or $this->Orientation() == '270' ) { return $this->{'Height'}; @@ -92,5 +99,18 @@ class Monitor { } return $this->{'Height'}; } + public function set( $data ) { + foreach ($data as $k => $v) { + if ( is_array( $v ) ) { + # perhaps should turn into a comma-separated string + $this->{$k} = implode(',',$v); + } else if ( is_string( $v ) ) { + $this->{$k} = trim( $v ); + } else { +Error( "Unknown type of var " . gettype( $v ) ); + $this->{$k} = $v; + } + } + } } ?> diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 1820b977d..546cb9cbe 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -26,6 +26,8 @@ class Storage { public function Path() { if ( isset( $this->{'Path'} ) and ( $this->{'Path'} != '' ) ) { return $this->{'Path'}; + } else if ( ! isset($this->{'Id'}) ) { + return ZM_DIR_EVENTS; } return $this->{'Name'}; } diff --git a/web/includes/actions.php b/web/includes/actions.php index 1ceda8231..74689c95c 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -527,6 +527,7 @@ if ( !empty($action) ) 'DoNativeMotDet' => 'toggle', 'Exif' => 'toggle', 'RTSPDescribe' => 'toggle', + 'RecordAudio' => 'toggle', ); $columns = getTableColumns( 'Monitors' ); diff --git a/web/includes/functions.php b/web/includes/functions.php index 9962f584b..517abd075 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -19,966 +19,928 @@ // // Compatibility functions -if ( version_compare( phpversion(), "4.3.0", "<") ) -{ - function ob_get_clean() - { - $buffer = ob_get_contents(); - ob_end_clean(); - return( $buffer ); - } +if ( version_compare( phpversion(), "4.3.0", "<") ) { + function ob_get_clean() { + $buffer = ob_get_contents(); + ob_end_clean(); + return( $buffer ); + } } -function userLogin( $username, $password="", $passwordHashed=false ) -{ - global $user, $cookies; +function userLogin( $username, $password="", $passwordHashed=false ) { + global $user, $cookies; - $sql = "select * from Users where Enabled = 1"; - $sql_values = NULL; - if ( ZM_AUTH_TYPE == "builtin" ) - { - if ( $passwordHashed ) { - $sql .= " AND Username=? AND Password=?"; - } else { - $sql .= " AND Username=? AND Password=password(?)"; - } - $sql_values = array( $username, $password ); + $sql = "select * from Users where Enabled = 1"; + $sql_values = NULL; + if ( ZM_AUTH_TYPE == "builtin" ) { + if ( $passwordHashed ) { + $sql .= " AND Username=? AND Password=?"; } else { - $sql .= " AND Username = ?"; - $sql_values = array( $username ); + $sql .= " AND Username=? AND Password=password(?)"; } - $_SESSION['username'] = $username; - if ( ZM_AUTH_RELAY == "plain" ) - { - // Need to save this in session - $_SESSION['password'] = $password; + $sql_values = array( $username, $password ); + } else { + $sql .= " AND Username = ?"; + $sql_values = array( $username ); + } + $_SESSION['username'] = $username; + if ( ZM_AUTH_RELAY == "plain" ) { + // Need to save this in session + $_SESSION['password'] = $password; + } + $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking + if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) { + Info( "Login successful for user \"$username\"" ); + $_SESSION['user'] = $user = $dbUser; + if ( ZM_AUTH_TYPE == "builtin" ) { + $_SESSION['passwordHash'] = $user['Password']; } - $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking - if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) - { - Info( "Login successful for user \"$username\"" ); - $_SESSION['user'] = $user = $dbUser; - if ( ZM_AUTH_TYPE == "builtin" ) - { - $_SESSION['passwordHash'] = $user['Password']; - } - } - else - { - Warning( "Login denied for user \"$username\"" ); - unset( $user ); - } - if ( $cookies ) - session_write_close(); -} - -function userLogout() -{ - global $user; - $username = $user['Username']; - - Info( "User \"$username\" logged out" ); - - unset( $_SESSION['user'] ); + } else { + Warning( "Login denied for user \"$username\"" ); unset( $user ); - - session_destroy(); + } + if ( $cookies ) + session_write_close(); } -function noCacheHeaders() -{ - header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past - header("Last-Modified: ".gmdate( "D, d M Y H:i:s" )." GMT"); // always modified - header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 - header("Cache-Control: post-check=0, pre-check=0", false); - header("Pragma: no-cache"); // HTTP/1.0 +function userLogout() { + global $user; + $username = $user['Username']; + + Info( "User \"$username\" logged out" ); + + unset( $_SESSION['user'] ); + unset( $user ); + + session_destroy(); +} + +function noCacheHeaders() { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past + header("Last-Modified: ".gmdate( "D, d M Y H:i:s" )." GMT"); // always modified + header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); // HTTP/1.0 } function CORSHeaders() { - if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) { + if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) { - # The following is left for future reference/use. - $valid = false; - $servers = dbFetchAll( 'SELECT * FROM Servers' ); - if ( sizeof($servers) <= 1 ) { - # Only need CORSHeaders in the event that there are multiple servers in use. - return; - } - foreach( dbFetchAll( 'SELECT * FROM Servers' ) as $row ) { - $Server = new Server( $row ); - if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { - $valid = true; - header("Access-Control-Allow-Origin: " . $Server->Url() ); - header("Access-Control-Allow-Headers: x-requested-with,x-request"); - } - } - if ( ! $valid ) { - Warning( $_SERVER['HTTP_ORIGIN'] . " is not found in servers list." ); - } - } +# The following is left for future reference/use. + $valid = false; + $servers = dbFetchAll( 'SELECT * FROM Servers' ); + if ( sizeof($servers) <= 1 ) { +# Only need CORSHeaders in the event that there are multiple servers in use. + return; + } + foreach( dbFetchAll( 'SELECT * FROM Servers' ) as $row ) { + $Server = new Server( $row ); + if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { + $valid = true; + header("Access-Control-Allow-Origin: " . $Server->Url() ); + header("Access-Control-Allow-Headers: x-requested-with,x-request"); + } + } + if ( ! $valid ) { + Warning( $_SERVER['HTTP_ORIGIN'] . " is not found in servers list." ); + } + } } -function getAuthUser( $auth ) -{ - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" && !empty($auth) ) +function getAuthUser( $auth ) { + if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" && !empty($auth) ) + { + $remoteAddr = ""; + if ( ZM_AUTH_HASH_IPS ) { + $remoteAddr = $_SERVER['REMOTE_ADDR']; + if ( !$remoteAddr ) + { + Error( "Can't determine remote address for authentication, using empty string" ); $remoteAddr = ""; - if ( ZM_AUTH_HASH_IPS ) - { - $remoteAddr = $_SERVER['REMOTE_ADDR']; - if ( !$remoteAddr ) - { - Error( "Can't determine remote address for authentication, using empty string" ); - $remoteAddr = ""; - } - } - - $sql = "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1"; - foreach ( dbFetchAll( $sql ) as $user ) - { - $now = time(); - for ( $i = 0; $i < 2; $i++, $now -= (60*60) ) // Try for last two hours - { - $time = localtime( $now ); - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5]; - $authHash = md5( $authKey ); - - if ( $auth == $authHash ) - { - return( $user ); - } - } - } + } } - Error( "Unable to authenticate user from auth hash '$auth'" ); - return( false ); + + $sql = "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1"; + foreach ( dbFetchAll( $sql ) as $user ) + { + $now = time(); + for ( $i = 0; $i < 2; $i++, $now -= (60*60) ) // Try for last two hours + { + $time = localtime( $now ); + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5]; + $authHash = md5( $authKey ); + + if ( $auth == $authHash ) + { + return( $user ); + } + } + } + } + Error( "Unable to authenticate user from auth hash '$auth'" ); + return( false ); } function generateAuthHash( $useRemoteAddr ) { - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" ) + if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" ) + { + $time = localtime(); + if ( $useRemoteAddr ) { - $time = localtime(); - if ( $useRemoteAddr ) - { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$time[2].$time[3].$time[4].$time[5]; - } - else - { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$time[2].$time[3].$time[4].$time[5]; - } - $auth = md5( $authKey ); + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$time[2].$time[3].$time[4].$time[5]; } else { - $auth = ""; + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$time[2].$time[3].$time[4].$time[5]; } - return( $auth ); + $auth = md5( $authKey ); + } + else + { + $auth = ""; + } + return( $auth ); } function getStreamSrc( $args, $querySep='&' ) { - $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; + $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; - if ( ZM_OPT_USE_AUTH ) + if ( ZM_OPT_USE_AUTH ) + { + if ( ZM_AUTH_RELAY == "hashed" ) { - if ( ZM_AUTH_RELAY == "hashed" ) - { - $args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); - } - elseif ( ZM_AUTH_RELAY == "plain" ) - { - $args[] = "user=".$_SESSION['username']; - $args[] = "pass=".$_SESSION['password']; - } - elseif ( ZM_AUTH_RELAY == "none" ) - { - $args[] = "user=".$_SESSION['username']; - } + $args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); } - if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) - { - $args[] = "connkey=".$GLOBALS['connkey']; - } - if ( ZM_RAND_STREAM ) + elseif ( ZM_AUTH_RELAY == "plain" ) { - $args[] = "rand=".time(); + $args[] = "user=".$_SESSION['username']; + $args[] = "pass=".$_SESSION['password']; } + elseif ( ZM_AUTH_RELAY == "none" ) + { + $args[] = "user=".$_SESSION['username']; + } + } + if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) + { + $args[] = "connkey=".$GLOBALS['connkey']; + } + if ( ZM_RAND_STREAM ) + { + $args[] = "rand=".time(); + } - if ( count($args) ) - { - $streamSrc .= "?".join( $querySep, $args ); - } + if ( count($args) ) + { + $streamSrc .= "?".join( $querySep, $args ); + } - return( $streamSrc ); + return( $streamSrc ); } function getMimeType( $file ) { - if ( function_exists('mime_content_type') ) - { - return( mime_content_type( $file ) ); - } - elseif ( function_exists('finfo_file') ) - { - $finfo = finfo_open( FILEINFO_MIME ); - $mimeType = finfo_file( $finfo, $file ); - finfo_close($finfo); - return( $mimeType ); - } - return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) ); + if ( function_exists('mime_content_type') ) + { + return( mime_content_type( $file ) ); + } + elseif ( function_exists('finfo_file') ) + { + $finfo = finfo_open( FILEINFO_MIME ); + $mimeType = finfo_file( $finfo, $file ); + finfo_close($finfo); + return( $mimeType ); + } + return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) ); } -function outputVideoStream( $id, $src, $width, $height, $format, $title="" ) -{ - if ( file_exists( $src ) ) - $mimeType = getMimeType( $src ); - else - { - switch( $format ) - { - case 'asf' : - $mimeType = "video/x-ms-asf"; - break; - case 'avi' : - case 'wmv' : - $mimeType = "video/x-msvideo"; - break; - case 'mov' : - $mimeType = "video/quicktime"; - break; - case 'mpg' : - case 'mpeg' : - $mimeType = "video/mpeg"; - break; - case 'swf' : - $mimeType = "application/x-shockwave-flash"; - break; - case '3gp' : - $mimeType = "video/3gpp"; - break; - default : - $mimeType = "video/$format"; - break; - } - } - if ( !$mimeType || ($mimeType == 'application/octet-stream') ) - $mimeType = 'video/'.$format; - $objectTag = false; - if ( ZM_WEB_USE_OBJECT_TAGS ) - { - switch( $mimeType ) - { - case "video/x-ms-asf" : - case "video/x-msvideo" : - case "video/mp4" : - { - if ( isWindows() ) - { -?> - - - - - - - - - - - - - - - - - - - - - - - - - -src="" -name="" -width="" -height="" -autostart="1" -autoplay="1" -showcontrols="0" -controller="0"> - - -