commit
a74b22a5ed
|
@ -63,6 +63,7 @@ set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING "MySQL engine to use with database, de
|
||||||
set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF")
|
set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF")
|
||||||
set(ZM_NO_FFMPEG "OFF" CACHE BOOL "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF")
|
set(ZM_NO_FFMPEG "OFF" CACHE BOOL "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF")
|
||||||
set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF")
|
set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF")
|
||||||
|
set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF")
|
||||||
set(ZM_NO_X10 "OFF" CACHE BOOL "Set to ON to build ZoneMinder without X10 support. default: OFF")
|
set(ZM_NO_X10 "OFF" CACHE BOOL "Set to ON to build ZoneMinder without X10 support. default: OFF")
|
||||||
set(ZM_PERL_SUBPREFIX "${CMAKE_INSTALL_LIBDIR}/perl5" CACHE PATH "Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into <prefix>/lib, default: <libdir>/perl5")
|
set(ZM_PERL_SUBPREFIX "${CMAKE_INSTALL_LIBDIR}/perl5" CACHE PATH "Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into <prefix>/lib, default: <libdir>/perl5")
|
||||||
set(ZM_PERL_USE_PATH "${CMAKE_INSTALL_PREFIX}/${ZM_PERL_SUBPREFIX}" CACHE PATH "Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: <prefix>/<zmperlsubprefix>")
|
set(ZM_PERL_USE_PATH "${CMAKE_INSTALL_PREFIX}/${ZM_PERL_SUBPREFIX}" CACHE PATH "Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: <prefix>/<zmperlsubprefix>")
|
||||||
|
@ -105,6 +106,22 @@ else(ZLIB_FOUND)
|
||||||
set(optlibsnotfound "${optlibsnotfound} zlib")
|
set(optlibsnotfound "${optlibsnotfound} zlib")
|
||||||
endif(ZLIB_FOUND)
|
endif(ZLIB_FOUND)
|
||||||
|
|
||||||
|
# Do not check for cURL if ZM_NO_CURL is on
|
||||||
|
if(NOT ZM_NO_CURL)
|
||||||
|
# cURL
|
||||||
|
find_package(CURL)
|
||||||
|
if(CURL_FOUND)
|
||||||
|
set(HAVE_LIBCURL 1)
|
||||||
|
list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
||||||
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
||||||
|
check_include_file("curl/curl.h" HAVE_CURL_CURL_H)
|
||||||
|
set(optlibsfound "${optlibsfound} cURL")
|
||||||
|
else(CURL_FOUND)
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} cURL")
|
||||||
|
endif(CURL_FOUND)
|
||||||
|
endif(NOT ZM_NO_CURL)
|
||||||
|
|
||||||
# jpeg
|
# jpeg
|
||||||
find_package(JPEG)
|
find_package(JPEG)
|
||||||
if(JPEG_FOUND)
|
if(JPEG_FOUND)
|
||||||
|
|
|
@ -278,6 +278,7 @@ AC_CHECK_LIB(swscale,sws_scale,,,-lswscale)
|
||||||
AC_CHECK_LIB(vlc,libvlc_new,,AC_MSG_WARN(libvlc.a may be required for streaming))
|
AC_CHECK_LIB(vlc,libvlc_new,,AC_MSG_WARN(libvlc.a may be required for streaming))
|
||||||
AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg))
|
AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg))
|
||||||
AC_CHECK_LIB(z,compress,,)
|
AC_CHECK_LIB(z,compress,,)
|
||||||
|
AC_CHECK_LIB(curl,curl_global_init,,)
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
AC_FUNC_ALLOCA
|
AC_FUNC_ALLOCA
|
||||||
|
@ -315,6 +316,7 @@ AC_CHECK_HEADERS(sys/shm.h,,,)
|
||||||
fi
|
fi
|
||||||
AC_CHECK_HEADERS(zlib.h,,,)
|
AC_CHECK_HEADERS(zlib.h,,,)
|
||||||
AC_CHECK_HEADERS(vlc/vlc.h,,,)
|
AC_CHECK_HEADERS(vlc/vlc.h,,,)
|
||||||
|
AC_CHECK_HEADERS(curl/curl.h,,,)
|
||||||
|
|
||||||
if test "$ZM_SSL_LIB" == "openssl"; then
|
if test "$ZM_SSL_LIB" == "openssl"; then
|
||||||
AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires openssl/md5.h - use ZM_SSL_LIB option to select gnutls instead]),[#include <stdlib.h>
|
AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires openssl/md5.h - use ZM_SSL_LIB option to select gnutls instead]),[#include <stdlib.h>
|
||||||
|
|
|
@ -63,7 +63,7 @@ DROP TABLE IF EXISTS `Controls`;
|
||||||
CREATE TABLE `Controls` (
|
CREATE TABLE `Controls` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`Type` enum('Local','Remote','Ffmpeg','Libvlc') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
|
||||||
`Protocol` varchar(64) default NULL,
|
`Protocol` varchar(64) default NULL,
|
||||||
`CanWake` tinyint(3) unsigned NOT NULL default '0',
|
`CanWake` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
|
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
@ -282,7 +282,7 @@ DROP TABLE IF EXISTS `MonitorPresets`;
|
||||||
CREATE TABLE `MonitorPresets` (
|
CREATE TABLE `MonitorPresets` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
|
||||||
`Device` tinytext,
|
`Device` tinytext,
|
||||||
`Channel` tinytext,
|
`Channel` tinytext,
|
||||||
`Format` int(10) unsigned default NULL,
|
`Format` int(10) unsigned default NULL,
|
||||||
|
@ -313,7 +313,7 @@ DROP TABLE IF EXISTS `Monitors`;
|
||||||
CREATE TABLE `Monitors` (
|
CREATE TABLE `Monitors` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
|
||||||
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
|
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
|
||||||
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
||||||
`LinkedMonitors` varchar(255) NOT NULL default '',
|
`LinkedMonitors` varchar(255) NOT NULL default '',
|
||||||
|
@ -327,6 +327,8 @@ CREATE TABLE `Monitors` (
|
||||||
`Port` varchar(8) NOT NULL default '',
|
`Port` varchar(8) NOT NULL default '',
|
||||||
`SubPath` varchar(64) NOT NULL default '',
|
`SubPath` varchar(64) NOT NULL default '',
|
||||||
`Path` varchar(255) NOT NULL default '',
|
`Path` varchar(255) NOT NULL default '',
|
||||||
|
`User` varchar(64) NOT NULL default '',
|
||||||
|
`Pass` varchar(64) NOT NULL default '',
|
||||||
`Width` smallint(5) unsigned NOT NULL default '0',
|
`Width` smallint(5) unsigned NOT NULL default '0',
|
||||||
`Height` smallint(5) unsigned NOT NULL default '0',
|
`Height` smallint(5) unsigned NOT NULL default '0',
|
||||||
`Colours` tinyint(3) unsigned NOT NULL default '1',
|
`Colours` tinyint(3) unsigned NOT NULL default '1',
|
||||||
|
|
|
@ -3,9 +3,15 @@
|
||||||
--
|
--
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Add Libvlc monitor type
|
-- Add Libvlc and cURL monitor types
|
||||||
--
|
--
|
||||||
|
|
||||||
ALTER TABLE Controls modify column Type enum('Local','Remote','Ffmpeg','Libvlc') NOT NULL default 'Local';
|
ALTER TABLE Controls modify column Type enum('Local','Remote','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local';
|
||||||
ALTER TABLE MonitorPresets modify column Type enum('Local','Remote','File','Ffmpeg','Libvlc') NOT NULL default 'Local';
|
ALTER TABLE MonitorPresets modify column Type enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local';
|
||||||
ALTER TABLE Monitors modify column Type enum('Local','Remote','File','Ffmpeg','Libvlc') NOT NULL default 'Local';
|
ALTER TABLE Monitors modify column Type enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Add required fields for cURL authenication
|
||||||
|
--
|
||||||
|
ALTER TABLE `Monitors` ADD `User` VARCHAR(32) NOT NULL AFTER `SubPath`;
|
||||||
|
ALTER TABLE `Monitors` ADD `Pass` VARCHAR(32) NOT NULL AFTER `User`;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
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)
|
# 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.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_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)
|
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_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)
|
||||||
|
|
||||||
# A fix for cmake recompiling the source files for every target.
|
# A fix for cmake recompiling the source files for every target.
|
||||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||||
|
|
|
@ -26,6 +26,7 @@ zm_SOURCES = \
|
||||||
zm_comms.cpp \
|
zm_comms.cpp \
|
||||||
zm_config.cpp \
|
zm_config.cpp \
|
||||||
zm_coord.cpp \
|
zm_coord.cpp \
|
||||||
|
zm_curl_camera.cpp \
|
||||||
zm.cpp \
|
zm.cpp \
|
||||||
zm_db.cpp \
|
zm_db.cpp \
|
||||||
zm_logger.cpp \
|
zm_logger.cpp \
|
||||||
|
@ -76,6 +77,7 @@ noinst_HEADERS = \
|
||||||
zm_config_defines.h \
|
zm_config_defines.h \
|
||||||
zm_config.h \
|
zm_config.h \
|
||||||
zm_coord.h \
|
zm_coord.h \
|
||||||
|
zm_curl_camera.h \
|
||||||
zm_db.h \
|
zm_db.h \
|
||||||
zm_logger.h \
|
zm_logger.h \
|
||||||
zm_event.h \
|
zm_event.h \
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
class Camera
|
class Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC } SourceType;
|
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
SourceType type;
|
SourceType type;
|
||||||
|
@ -59,6 +59,7 @@ public:
|
||||||
bool IsFile() const { return( type == FILE_SRC ); }
|
bool IsFile() const { return( type == FILE_SRC ); }
|
||||||
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
||||||
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
||||||
|
bool IscURL() const { return( type == CURL_SRC ); }
|
||||||
unsigned int Width() const { return( width ); }
|
unsigned int Width() const { return( width ); }
|
||||||
unsigned int Height() const { return( height ); }
|
unsigned int Height() const { return( height ); }
|
||||||
unsigned int Colours() const { return( colours ); }
|
unsigned int Colours() const { return( colours ); }
|
||||||
|
|
|
@ -0,0 +1,563 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder cURL Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// 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_curl_camera.h"
|
||||||
|
|
||||||
|
#if HAVE_LIBCURL
|
||||||
|
|
||||||
|
#define CURL_MAXRETRY 5
|
||||||
|
#define CURL_BUFFER_INITIAL_SIZE 65536
|
||||||
|
|
||||||
|
const char* content_length_match = "Content-Length:";
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( capture )
|
||||||
|
{
|
||||||
|
Initialise();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cURLCamera::~cURLCamera()
|
||||||
|
{
|
||||||
|
if ( capture )
|
||||||
|
{
|
||||||
|
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cURLCamera::Initialise()
|
||||||
|
{
|
||||||
|
content_length_match_len = strlen(content_length_match);
|
||||||
|
content_type_match_len = strlen(content_type_match);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
/* Wait for thread termination */
|
||||||
|
pthread_join(thread, NULL);
|
||||||
|
|
||||||
|
/* Destroy condition variables */
|
||||||
|
pthread_cond_destroy(&request_complete_cond);
|
||||||
|
pthread_cond_destroy(&data_available_cond);
|
||||||
|
|
||||||
|
/* Destroy mutex */
|
||||||
|
pthread_mutex_destroy(&shareddata_mutex);
|
||||||
|
|
||||||
|
/* cURL cleanup */
|
||||||
|
curl_global_cleanup();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::PrimeCapture()
|
||||||
|
{
|
||||||
|
//Info( "Priming capture from %s", mPath.c_str() );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::PreCapture()
|
||||||
|
{
|
||||||
|
// Nothing to do here
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::Capture( Image &image )
|
||||||
|
{
|
||||||
|
bool frameComplete = false;
|
||||||
|
uint8_t* directbuffer;
|
||||||
|
|
||||||
|
/* MODE_STREAM specific variables */
|
||||||
|
bool SubHeadersParsingComplete = false;
|
||||||
|
unsigned int frame_content_length = 0;
|
||||||
|
std::string frame_content_type;
|
||||||
|
bool need_more_data = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grab the mutex to ensure exclusive access to the shared data */
|
||||||
|
lock();
|
||||||
|
|
||||||
|
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(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) {
|
||||||
|
|
||||||
|
/* Subheader parsing */
|
||||||
|
while(!SubHeadersParsingComplete && !need_more_data) {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* 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 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);
|
||||||
|
|
||||||
|
/* Advance the buffer past this one */
|
||||||
|
databuffer.consume(crlf_start+crlf_size);
|
||||||
|
|
||||||
|
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(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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());
|
||||||
|
|
||||||
|
/* Check the header */
|
||||||
|
if(subheader.compare(0,content_length_match_len,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(subheader.compare(0,content_type_match_len,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 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 */
|
||||||
|
|
||||||
|
} /* frameComplete loop */
|
||||||
|
|
||||||
|
/* Release the mutex */
|
||||||
|
unlock();
|
||||||
|
|
||||||
|
if(!frameComplete)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::PostCapture()
|
||||||
|
{
|
||||||
|
// Nothing to do here
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||||
|
{
|
||||||
|
lock();
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
|
||||||
|
/* 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());
|
||||||
|
|
||||||
|
/* 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(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content_type = header.substr(pos+1, std::string::npos);
|
||||||
|
Debug(6,"Content-Type is: %s",content_type.c_str());
|
||||||
|
|
||||||
|
lock();
|
||||||
|
|
||||||
|
const char* multipart_match = "multipart/x-mixed-replace";
|
||||||
|
const char* image_jpeg_match = "image/jpeg";
|
||||||
|
if(content_type.compare(0, strlen(multipart_match), multipart_match) == 0) {
|
||||||
|
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
||||||
|
mode = MODE_STREAM;
|
||||||
|
} else if(content_type.compare(0, strlen(image_jpeg_match), image_jpeg_match) == 0) {
|
||||||
|
Debug(7,"Content type matched as image/jpeg");
|
||||||
|
mode = MODE_SINGLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return bytes processed */
|
||||||
|
return size*nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* cURLCamera::thread_func()
|
||||||
|
{
|
||||||
|
int tRet;
|
||||||
|
double dSize;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::lock() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cURLCamera::unlock() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* thread_func_dispatcher(void* object) {
|
||||||
|
return ((cURLCamera*)object)->thread_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // HAVE_LIBCURL
|
|
@ -0,0 +1,105 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder cURL Class Interface, $Date: 2008-07-25 10:33:23 +0100 (Fri, 25 Jul 2008) $, $Revision: 2611 $
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// 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_CURL_CAMERA_H
|
||||||
|
#define ZM_CURL_CAMERA_H
|
||||||
|
|
||||||
|
#if HAVE_LIBCURL
|
||||||
|
|
||||||
|
#include "zm_camera.h"
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
#include "zm_buffer.h"
|
||||||
|
#include "zm_regexp.h"
|
||||||
|
#include "zm_utils.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#if HAVE_CURL_CURL_H
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Class representing 'remote' cameras, i.e. those which are
|
||||||
|
// accessed over a network connection.
|
||||||
|
//
|
||||||
|
class cURLCamera : public Camera
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
|
||||||
|
|
||||||
|
std::string mPath;
|
||||||
|
std::string mUser;
|
||||||
|
std::string mPass;
|
||||||
|
|
||||||
|
/* cURL object(s) */
|
||||||
|
CURL* c;
|
||||||
|
|
||||||
|
/* Shared data */
|
||||||
|
volatile bool bTerminate;
|
||||||
|
volatile bool bReset;
|
||||||
|
volatile mode_t mode;
|
||||||
|
Buffer databuffer;
|
||||||
|
std::deque<size_t> single_offsets;
|
||||||
|
|
||||||
|
/* 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();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
int PrimeCapture();
|
||||||
|
int PreCapture();
|
||||||
|
int Capture( Image &image );
|
||||||
|
int PostCapture();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Dispatchers */
|
||||||
|
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||||
|
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||||
|
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||||
|
void* thread_func_dispatcher(void* object);
|
||||||
|
|
||||||
|
#endif // HAVE_LIBCURL
|
||||||
|
|
||||||
|
#endif // ZM_CURL_CAMERA_H
|
|
@ -43,6 +43,9 @@
|
||||||
#if HAVE_LIBVLC
|
#if HAVE_LIBVLC
|
||||||
#include "zm_libvlc_camera.h"
|
#include "zm_libvlc_camera.h"
|
||||||
#endif // HAVE_LIBVLC
|
#endif // HAVE_LIBVLC
|
||||||
|
#if HAVE_LIBCURL
|
||||||
|
#include "zm_curl_camera.h"
|
||||||
|
#endif // HAVE_LIBCURL
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
@ -2422,7 +2425,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
||||||
Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose )
|
Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose )
|
||||||
{
|
{
|
||||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||||
snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id );
|
snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, Protocol, Method, Host, Port, Path, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id );
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
|
@ -2458,6 +2461,8 @@ Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose )
|
||||||
std::string host = dbrow[col]; col++;
|
std::string host = dbrow[col]; col++;
|
||||||
std::string port = dbrow[col]; col++;
|
std::string port = dbrow[col]; col++;
|
||||||
std::string path = dbrow[col]; col++;
|
std::string path = dbrow[col]; col++;
|
||||||
|
std::string user = dbrow[col]; col++;
|
||||||
|
std::string pass = dbrow[col]; col++;
|
||||||
|
|
||||||
int width = atoi(dbrow[col]); col++;
|
int width = atoi(dbrow[col]); col++;
|
||||||
int height = atoi(dbrow[col]); col++;
|
int height = atoi(dbrow[col]); col++;
|
||||||
|
@ -2626,6 +2631,27 @@ Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose )
|
||||||
#else // HAVE_LIBVLC
|
#else // HAVE_LIBVLC
|
||||||
Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id );
|
Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id );
|
||||||
#endif // HAVE_LIBVLC
|
#endif // HAVE_LIBVLC
|
||||||
|
}
|
||||||
|
else if ( type == "cURL" )
|
||||||
|
{
|
||||||
|
#if HAVE_LIBCURL
|
||||||
|
camera = new cURLCamera(
|
||||||
|
id,
|
||||||
|
path.c_str(),
|
||||||
|
user.c_str(),
|
||||||
|
pass.c_str(),
|
||||||
|
cam_width,
|
||||||
|
cam_height,
|
||||||
|
colours,
|
||||||
|
brightness,
|
||||||
|
contrast,
|
||||||
|
hue,
|
||||||
|
colour,
|
||||||
|
purpose==CAPTURE
|
||||||
|
);
|
||||||
|
#else // HAVE_LIBCURL
|
||||||
|
Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id );
|
||||||
|
#endif // HAVE_LIBCURL
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -313,6 +313,8 @@ foreach( $displayMonitors as $monitor )
|
||||||
$shortpath = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
$shortpath = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||||
?>
|
?>
|
||||||
<td class="colSource"><?= makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$shortpath.'</span>', canEdit( 'Monitors' ) ) ?></td>
|
<td class="colSource"><?= makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$shortpath.'</span>', canEdit( 'Monitors' ) ) ?></td>
|
||||||
|
<?php } elseif ( $monitor['Type'] == "cURL" ) { ?>
|
||||||
|
<td class="colSource"><?= makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
<td class="colSource"> </td>
|
<td class="colSource"> </td>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
|
@ -345,7 +345,7 @@ switch ( $tab )
|
||||||
?>
|
?>
|
||||||
<tr><th scope="row"><?= $SLANG['Name'] ?></th><td><input type="text" name="newControl[Name]" value="<?= validHtmlStr($newControl['Name']) ?>" size="24"/></td></tr>
|
<tr><th scope="row"><?= $SLANG['Name'] ?></th><td><input type="text" name="newControl[Name]" value="<?= validHtmlStr($newControl['Name']) ?>" size="24"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
$types = array( 'Local'=>$SLANG['Local'], 'Remote'=>$SLANG['Remote'], 'Ffmpeg'=>$SLANG['Ffmpeg'], 'Libvlc'=>$SLANG['Libvlc'] );
|
$types = array( 'Local'=>$SLANG['Local'], 'Remote'=>$SLANG['Remote'], 'Ffmpeg'=>$SLANG['Ffmpeg'], 'Libvlc'=>$SLANG['Libvlc'], 'cURL'=>"cURL");
|
||||||
?>
|
?>
|
||||||
<tr><th scope="row"><?= $SLANG['Type'] ?></th><td><?= buildSelect( "newControl[Type]", $types ); ?></td></tr>
|
<tr><th scope="row"><?= $SLANG['Type'] ?></th><td><?= buildSelect( "newControl[Type]", $types ); ?></td></tr>
|
||||||
<tr><th scope="row"><?= $SLANG['Protocol'] ?></th><td><input type="text" name="newControl[Protocol]" value="<?= validHtmlStr($newControl['Protocol']) ?>" size="24"/></td></tr>
|
<tr><th scope="row"><?= $SLANG['Protocol'] ?></th><td><input type="text" name="newControl[Protocol]" value="<?= validHtmlStr($newControl['Protocol']) ?>" size="24"/></td></tr>
|
||||||
|
|
|
@ -64,6 +64,8 @@ else
|
||||||
'Host' => "",
|
'Host' => "",
|
||||||
'Path' => "",
|
'Path' => "",
|
||||||
'Port' => "80",
|
'Port' => "80",
|
||||||
|
'User' => "",
|
||||||
|
'Pass' => "",
|
||||||
'Colours' => 3,
|
'Colours' => 3,
|
||||||
'Palette' => 0,
|
'Palette' => 0,
|
||||||
'Width' => "320",
|
'Width' => "320",
|
||||||
|
@ -176,6 +178,7 @@ $sourceTypes = array(
|
||||||
'File' => $SLANG['File'],
|
'File' => $SLANG['File'],
|
||||||
'Ffmpeg' => $SLANG['Ffmpeg'],
|
'Ffmpeg' => $SLANG['Ffmpeg'],
|
||||||
'Libvlc' => $SLANG['Libvlc'],
|
'Libvlc' => $SLANG['Libvlc'],
|
||||||
|
'cURL' => "cURL (HTTP only)"
|
||||||
);
|
);
|
||||||
if ( !ZM_HAS_V4L )
|
if ( !ZM_HAS_V4L )
|
||||||
unset($sourceTypes['Local']);
|
unset($sourceTypes['Local']);
|
||||||
|
@ -503,10 +506,12 @@ if ( $tab != 'source' || ($newMonitor['Type'] != 'Local' && $newMonitor['Type']
|
||||||
<input type="hidden" name="newMonitor[Method]" value="<?= validHtmlStr($newMonitor['Method']) ?>"/>
|
<input type="hidden" name="newMonitor[Method]" value="<?= validHtmlStr($newMonitor['Method']) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Type'] != 'File' && $newMonitor['Type'] != 'Ffmpeg' && $newMonitor['Type'] != 'Libvlc') )
|
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Type'] != 'File' && $newMonitor['Type'] != 'Ffmpeg' && $newMonitor['Type'] != 'Libvlc' && $newMonitor['Type'] != 'cURL') )
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<input type="hidden" name="newMonitor[Path]" value="<?= validHtmlStr($newMonitor['Path']) ?>"/>
|
<input type="hidden" name="newMonitor[Path]" value="<?= validHtmlStr($newMonitor['Path']) ?>"/>
|
||||||
|
<input type="hidden" name="newMonitor[User]" value="<?= validHtmlStr($newMonitor['User']) ?>"/>
|
||||||
|
<input type="hidden" name="newMonitor[Pass]" value="<?= validHtmlStr($newMonitor['Pass']) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' )
|
if ( $tab != 'source' )
|
||||||
|
@ -720,6 +725,14 @@ switch ( $tab )
|
||||||
{
|
{
|
||||||
?>
|
?>
|
||||||
<tr><td><?= $SLANG['SourcePath'] ?></td><td><input type="text" name="newMonitor[Path]" value="<?= validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
<tr><td><?= $SLANG['SourcePath'] ?></td><td><input type="text" name="newMonitor[Path]" value="<?= validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
elseif ( $newMonitor['Type'] == "cURL" )
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<tr><td><?= "Address" ?></td><td><input type="text" name="newMonitor[Path]" value="<?= validHtmlStr($newMonitor['Path']) ?>" size="36"/></td></tr>
|
||||||
|
<tr><td><?= "Username" ?></td><td><input type="text" name="newMonitor[User]" value="<?= validHtmlStr($newMonitor['User']) ?>" size="12"/></td></tr>
|
||||||
|
<tr><td><?= "Password" ?></td><td><input type="text" name="newMonitor[Pass]" value="<?= validHtmlStr($newMonitor['Pass']) ?>" size="12"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
/* Library checks and their header files */
|
/* Library checks and their header files */
|
||||||
#cmakedefine HAVE_LIBZLIB 1
|
#cmakedefine HAVE_LIBZLIB 1
|
||||||
#cmakedefine HAVE_ZLIB_H 1
|
#cmakedefine HAVE_ZLIB_H 1
|
||||||
|
#cmakedefine HAVE_LIBCURL 1
|
||||||
|
#cmakedefine HAVE_CURL_CURL_H 1
|
||||||
#cmakedefine HAVE_LIBJPEG 1
|
#cmakedefine HAVE_LIBJPEG 1
|
||||||
#cmakedefine HAVE_JPEGLIB_H 1
|
#cmakedefine HAVE_JPEGLIB_H 1
|
||||||
#cmakedefine HAVE_LIBOPENSSL 1
|
#cmakedefine HAVE_LIBOPENSSL 1
|
||||||
|
|
Loading…
Reference in New Issue