Merge branch 'master' of github.com:/ZoneMinder/ZoneMinder

This commit is contained in:
Isaac Connor 2019-04-29 12:50:31 -04:00
commit 847d4c5e80
40 changed files with 977 additions and 910 deletions

View File

@ -3,6 +3,147 @@ Debian
.. contents::
Easy Way: Debian Stretch
------------------------
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.32.3 on Debian 9.8.
**Step 1:** Make sure your system is up to date
Open a console and use ``su`` command to become Root.
::
apt update
apt upgrade
**Step 2:** Setup Sudo (optional but recommended)
By default Debian does not come with sudo, so you have to install it and configure it manually. This step is optional but recommended and the following instructions assume that you have setup sudo. If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
::
apt install sudo
usermod -a -G sudo <username>
exit
Now your terminal session is back under your normal user. You can check that you are now part of the sudo group with the command ``groups``, "sudo" should appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
**Step 3:** Install Apache and MySQL
These are not dependencies for the ZoneMinder package as they could be installed elsewhere. If they are not installed yet in your system, you have to trigger their installation manually.
::
sudo apt install apache2 mysql-server
**Step 4:** Add ZoneMinder's Package repository to your apt sources
ZoneMinder's Debian packages are not included in Debian's official package repositories. To be able to install ZoneMinder with APT, you have to edit the list of apt sources and add ZoneMinder's repository.
::
sudo nano /etc/apt/sources.list
Add the following to the bottom of the file
::
# ZoneMinder repository
deb https://zmrepo.zoneminder.com/debian/release stretch/
CTRL+o and <Enter> to save
CTRL+x to exit
Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
::
sudo apt install apt-transport-https
Finally, download the GPG key for ZoneMinder's repository:
::
wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
**Step 5:** Install ZoneMinder
::
sudo apt update
sudo apt install zoneminder
**Step 6:** Read the Readme
The rest of the install process is covered in the README.Debian, so feel free to have
a read.
::
gunzip /usr/share/doc/zoneminder/README.Debian.gz
cat /usr/share/doc/zoneminder/README.Debian
**Step 7:** Enable ZoneMinder service
::
sudo systemctl enable zoneminder.service
**Step 8:** Configure Apache
The following commands will setup the default /zm virtual directory and configure
required apache modules.
::
sudo a2enconf zoneminder
sudo a2enmod rewrite
sudo a2enmod cgi # this is done automatically when installing the package. Redo this command manually only for troubleshooting.
**Step 9:** Edit Timezone in PHP
Automated way:
::
sudo sed -i "s/;date.timezone =/date.timezone = $(sed 's/\//\\\//' /etc/timezone)/g" /etc/php/7.0/apache2/php.ini
Manual way
::
sudo nano /etc/php/7.0/apache2/php.ini
Search for [Date] (Ctrl + w then type Date and press Enter) and change
date.timezone for your time zone. Don't forget to remove the ; from in front
of date.timezone.
::
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = America/New_York
CTRL+o then [Enter] to save
CTRL+x to exit
**Step 10:** Start ZoneMinder
Reload Apache to enable your changes and then start ZoneMinder.
::
sudo systemctl reload apache2
sudo systemctl start zoneminder
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
Easy Way: Debian Jessie
-----------------------
@ -81,7 +222,7 @@ should you choose to change default database user and password.
cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf
echo 'grant lock tables,alter,create,select,insert,update,delete,index on zm.* to 'zmuser'@localhost identified by "zmpass";' | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
** Step 8:** zm.conf Permissions
**Step 8:** zm.conf Permissions
Adjust permissions to the zm.conf file to allow web account to access it.
@ -129,6 +270,7 @@ CTRL+x to exit
**Step 12:** Please check the configuration
Zoneminder 1.32.x
1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms
::

View File

@ -11,4 +11,4 @@ A fast video interface core, a user-friendly and comprehensive PHP based web int
The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones.
ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit http://www.zoneminder.com/donate.html and help us fund our future improvements.
ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit https://zoneminder.com/donate/ and help us fund our future improvements.

View File

@ -252,8 +252,6 @@ void Logger::initialise(const std::string &id, const Options &options) {
}
void Logger::terminate() {
Debug(1, "Terminating Logger");
if ( mFileLevel > NOLOG )
closeFile();
@ -574,6 +572,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
free(filecopy);
if ( level <= FATAL ) {
log_mutex.unlock();
logTerm();
zmDbClose();
if ( level <= PANIC )

File diff suppressed because it is too large Load Diff

View File

@ -291,40 +291,42 @@ protected:
bool last_signal;
double fps;
unsigned int last_camera_bytes;
double fps;
unsigned int last_camera_bytes;
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
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
std::string diag_path_r;
std::string diag_path_d;
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;
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;
#if ZM_MEM_MAPPED
int map_fd;
char mem_file[PATH_MAX];
int map_fd;
char mem_file[PATH_MAX];
#else // ZM_MEM_MAPPED
int shm_id;
int shm_id;
#endif // ZM_MEM_MAPPED
off_t mem_size;
unsigned char *mem_ptr;
SharedData *shared_data;
TriggerData *trigger_data;
off_t mem_size;
unsigned char *mem_ptr;
SharedData *shared_data;
TriggerData *trigger_data;
VideoStoreData *video_store_data;
Snapshot *image_buffer;

View File

@ -28,7 +28,22 @@
#include <sys/types.h>
#include <sys/socket.h>
RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_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 ) :
RemoteCameraRtsp::RemoteCameraRtsp(
unsigned int p_monitor_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_monitor_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 )
@ -63,13 +78,13 @@ RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string
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) {
if ( colours == ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA;
} else if(colours == ZM_COLOUR_RGB24) {
} else if ( colours == ZM_COLOUR_RGB24 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGB;
imagePixFormat = AV_PIX_FMT_RGB24;
} else if(colours == ZM_COLOUR_GRAY8) {
} else if ( colours == ZM_COLOUR_GRAY8 ) {
subpixelorder = ZM_SUBPIX_ORDER_NONE;
imagePixFormat = AV_PIX_FMT_GRAY8;
} else {
@ -169,7 +184,7 @@ int RemoteCameraRtsp::PrimeCapture() {
} else {
Debug(2, "Have another video stream." );
}
}
} else
#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
@ -181,6 +196,8 @@ int RemoteCameraRtsp::PrimeCapture() {
} else {
Debug(2, "Have another audio stream." );
}
} else {
Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type);
}
} // end foreach stream

View File

@ -34,8 +34,7 @@
// accessed over a network connection using rtsp protocol
// (Real Time Streaming Protocol)
//
class RemoteCameraRtsp : public RemoteCamera
{
class RemoteCameraRtsp : public RemoteCamera {
protected:
struct sockaddr_in rtsp_sa;
struct sockaddr_in rtcp_sa;

View File

@ -28,12 +28,12 @@
#include <errno.h>
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource )
: mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
{
}
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) {
const RtcpPacket *rtcpPacket;
rtcpPacket = (RtcpPacket *)packet;
@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
int pt = rtcpPacket->header.pt;
int len = ntohs(rtcpPacket->header.lenN);
Debug( 5, "RTCP Ver: %d", ver );
Debug( 5, "RTCP Count: %d", count );
Debug( 5, "RTCP Pt: %d", pt );
Debug( 5, "RTCP len: %d", len );
Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len);
switch( pt )
{
switch( pt ) {
case RTCP_SR :
{
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
Debug( 5, "RTCP Got SR (%x)", ssrc );
if ( mRtpSource.getSsrc() )
{
if ( ssrc != mRtpSource.getSsrc() )
{
if ( mRtpSource.getSsrc() ) {
if ( ssrc != mRtpSource.getSsrc() ) {
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
}
else if ( ssrc )
{
} else if ( ssrc ) {
mRtpSource.setSsrc( ssrc );
}
if ( len > 1 )
{
if ( len > 1 ) {
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
@ -89,25 +80,21 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
case RTCP_SDES :
{
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
while ( contentLen )
{
while ( contentLen ) {
Debug( 5, "RTCP CL: %zd", contentLen );
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
{
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) {
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
for ( int i = 0; i < count; i++ )
{
for ( int i = 0; i < count; i++ ) {
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
Debug( 5, "RTCP Item length %d", item->len );
switch( item->type )
{
switch( item->type ) {
case RTCP_SDES_CNAME :
{
std::string cname( item->data, item->len );
@ -123,50 +110,39 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
case RTCP_SDES_NOTE :
case RTCP_SDES_PRIV :
default :
{
Error( "Received unexpected SDES item type %d, ignoring", item->type );
return( -1 );
}
return -1;
}
int paddedLen = 4+2+item->len+1; // Add null byte
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
Debug( 5, "RTCP PL:%d", paddedLen );
Debug(5, "RTCP PL:%d", paddedLen);
sdesPtr += paddedLen;
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
}
}
} // end whiel contentLen
break;
}
case RTCP_BYE :
{
Debug( 5, "RTCP Got BYE" );
Debug(5, "RTCP Got BYE");
mStop = true;
break;
}
case RTCP_APP :
{
// Ignoring as per RFC 3550
Debug( 5, "Received RTCP_APP packet, ignoring.");
Debug(5, "Received RTCP_APP packet, ignoring.");
break;
}
case RTCP_RR :
{
Error( "Received RTCP_RR packet." );
return( -1 );
}
Error("Received RTCP_RR packet.");
return -1;
default :
{
// Ignore unknown packet types. Some cameras do this by design.
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
Debug(5, "Received unexpected packet type %d, ignoring", pt);
break;
}
}
consumed = sizeof(uint32_t)*(len+1);
return( consumed );
return consumed;
}
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
@ -180,11 +156,13 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
mRtpSource.updateRtcpStats();
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d",
mRtspThread.getSsrc()+1,
mRtpSource.getSsrc(),
mRtpSource.getMaxSeq(),
mRtpSource.getJitter(),
mRtpSource.getLastSrTimestamp()
);
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
@ -195,11 +173,10 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
rtcpPacket->body.rr.rr[0].dlsrN = 0;
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrlThread::generateRr
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
const std::string &cname = mRtpSource.getCname();
@ -218,11 +195,10 @@ int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen
rtcpPacket->body.sdes.item[0].len = cname.size();
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrlThread::generateSdes
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
@ -236,11 +212,10 @@ int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrolThread::generateBye
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
{
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) {
unsigned char *bufferPtr = buffer;
// u_int32 len; /* length of compound RTCP packet in words */
@ -259,33 +234,28 @@ int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
// /* something wrong with packet format */
// }
while ( nBytes > 0 )
{
while ( nBytes > 0 ) {
int consumed = recvPacket( bufferPtr, nBytes );
if ( consumed <= 0 )
break;
bufferPtr += consumed;
nBytes -= consumed;
}
return( nBytes );
return nBytes;
}
int RtpCtrlThread::run()
{
int RtpCtrlThread::run() {
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
SockAddrInet localAddr, remoteAddr;
bool sendReports;
UdpInetSocket rtpCtrlServer;
if ( mRtpSource.getLocalHost() != "" )
{
if ( mRtpSource.getLocalHost() != "" ) {
if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" );
sendReports = false;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
}
else
{
} else {
if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
@ -309,17 +279,17 @@ int RtpCtrlThread::run()
time_t now = time(NULL);
Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 )
{
if ( readable.size() == 0 ) {
if ( ! timeout ) {
// With this code here, we will send an SDES and RR packet every 10 seconds
ssize_t nBytes;
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
Error( "Unable to send: %s", strerror( errno ) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d",
bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
Error("Unable to send: %s", strerror(errno));
timeout = true;
continue;
} else {
@ -332,25 +302,21 @@ int RtpCtrlThread::run()
timeout = false;
last_receive = time(NULL);
}
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter )
{
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
{
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) ) {
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes )
{
if ( nBytes ) {
recvPackets( buffer, nBytes );
if ( sendReports )
{
if ( sendReports ) {
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
Debug(3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc());
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
Error( "Unable to send: %s", strerror( errno ) );
Error("Unable to send: %s", strerror(errno));
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
}
} else {
@ -358,16 +324,14 @@ int RtpCtrlThread::run()
mStop = true;
break;
}
}
else
{
Panic( "Barfed" );
}
}
} else {
Panic("Barfed");
} // end if socket
} // end foeach comms iterator
}
rtpCtrlServer.close();
mRtspThread.stop();
return( 0 );
return 0;
}
#endif // HAVE_LIBAVFORMAT

View File

@ -34,13 +34,11 @@
class RtspThread;
class RtpSource;
class RtpCtrlThread : public Thread
{
class RtpCtrlThread : public Thread {
friend class RtspThread;
private:
typedef enum
{
typedef enum {
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
@ -48,8 +46,7 @@ private:
RTCP_APP = 204
} RtcpType;
typedef enum
{
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
@ -61,8 +58,7 @@ private:
RTCP_SDES_PRIV = 8
} RtcpSdesType;
struct RtcpCommonHeader
{
struct RtcpCommonHeader {
uint8_t count:5; // varies by packet type
uint8_t p:1; // padding flag
uint8_t version:2; // protocol version
@ -71,8 +67,7 @@ private:
};
// Reception report block
struct RtcpRr
{
struct RtcpRr {
uint32_t ssrcN; // data source being reported
int32_t lost:24; // cumul. no. pkts lost (signed!)
uint32_t fraction:8; // fraction lost since last SR/RR
@ -83,22 +78,18 @@ private:
};
// SDES item
struct RtcpSdesItem
{
struct RtcpSdesItem {
uint8_t type; // type of item (rtcp_sdes_type_t)
uint8_t len; // length of item (in octets)
char data[]; // text, not null-terminated
};
// RTCP packet
struct RtcpPacket
{
struct RtcpPacket {
RtcpCommonHeader header; // common header
union
{
union {
// Sender Report (SR)
struct Sr
{
struct Sr {
uint32_t ssrcN; // sender generating this report, network order
uint32_t ntpSecN; // NTP timestamp, network order
uint32_t ntpFracN;
@ -109,22 +100,19 @@ private:
} sr;
// Reception Report (RR)
struct Rr
{
struct Rr {
uint32_t ssrcN; // receiver generating this report
RtcpRr rr[]; // variable-length list
} rr;
// source description (SDES)
struct Sdes
{
struct Sdes {
uint32_t srcN; // first SSRC/CSRC
RtcpSdesItem item[]; // list of SDES items
} sdes;
// BYE
struct
{
struct {
uint32_t srcN[]; // list of sources
// can't express trailing text for reason (what does this mean? it's not even english!)
} bye;
@ -148,8 +136,7 @@ private:
public:
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
void stop()
{
void stop() {
mStop = true;
}
};

View File

@ -26,7 +26,17 @@
#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 ) :
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 ),
@ -65,13 +75,12 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c
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." );
if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 )
Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId);
}
void RtpSource::init( uint16_t seq )
{
Debug( 3, "Initialising sequence" );
void RtpSource::init( uint16_t seq ) {
Debug(3, "Initialising sequence");
mBaseSeq = seq;
mMaxSeq = seq;
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq )
mTransit = 0;
}
bool RtpSource::updateSeq( uint16_t seq )
{
bool RtpSource::updateSeq( uint16_t seq ) {
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 );
if ( mProbation)
{
if ( mProbation) {
// packet is in sequence
if ( seq == mMaxSeq + 1)
{
if ( seq == mMaxSeq + 1) {
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
mProbation--;
mMaxSeq = seq;
if ( mProbation == 0 )
{
if ( mProbation == 0 ) {
init( seq );
mReceivedPackets++;
return( true );
}
}
else
{
} else {
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 )
{
} else if ( uDelta < MAX_DROPOUT ) {
if ( uDelta == 1 ) {
Debug( 4, "Packet in sequence, gap %d", uDelta );
}
else
{
} else {
Warning( "Packet in sequence, gap %d", uDelta );
}
// in order, with permissible gap
if ( seq < mMaxSeq )
{
if ( seq < mMaxSeq ) {
// Sequence number wrapped - count another 64K cycle.
mCycles += RTP_SEQ_MOD;
}
mMaxSeq = seq;
}
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
{
} 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 )
{
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
{
} else {
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
return( false );
}
}
else
{
} else {
Warning( "Packet duplicate or reordered, gap %d", uDelta );
// duplicate or reordered packet
return( false );
@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq )
return( uDelta==1?true:false );
}
void RtpSource::updateJitter( const RtpDataHeader *header )
{
if ( mRtpFactor > 0 )
{
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 );
@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
Debug( 5, "Packet transit RTP time = %x", packetTransit );
if ( mTransit > 0 )
{
if ( mTransit > 0 ) {
// Jitter
int d = packetTransit - mTransit;
Debug( 5, "Jitter D = %d", d );
@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
mJitter += d - ((mJitter + 8) >> 4);
}
mTransit = packetTransit;
}
else
{
} else {
mJitter = 0;
}
Debug( 5, "RTP Jitter: %d", mJitter );
}
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
{
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)) );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
if ( mBaseTimeNtp.tv_sec == 0 )
{
if ( mBaseTimeNtp.tv_sec == 0 ) {
mBaseTimeReal = tvNow();
mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime;
}
else if ( !mRtpClock )
{
} 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 );
@ -227,8 +208,7 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint
mLastSrTimeRtp = rtpTime;
}
void RtpSource::updateRtcpStats()
{
void RtpSource::updateRtcpStats() {
uint32_t extendedMax = mCycles + mMaxSeq;
mExpectedPackets = extendedMax - mBaseSeq + 1;
@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats()
Debug( 5, "Lost fraction = %d", mLostFraction );
}
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
{
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
const RtpDataHeader *rtpHeader;
rtpHeader = (RtpDataHeader *)packet;
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
@ -269,39 +248,29 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
// 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) ) )
{
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) {
Hexdump( 4, packet+rtpHeaderSize, 16 );
if ( mFrameGood )
{
if ( mFrameGood ) {
int extraHeader = 0;
if( mCodecId == AV_CODEC_ID_H264 )
{
if ( mCodecId == AV_CODEC_ID_H264 ) {
int nalType = (packet[rtpHeaderSize] & 0x1f);
Debug( 3, "Have H264 frame: nal type is %d", nalType );
switch (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 )
{
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
@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
extraHeader = 2;
break;
}
default:
{
Debug(3, "Unhandled nalType %d", nalType );
}
}
// Append NAL frame start code
if ( !mFrame.size() )
mFrame.append( "\x0\x0\x1", 3 );
}
} // end if H264
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
} else {
Debug( 3, "NOT H264 frame: type is %d", mCodecId );
@ -329,16 +295,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
Hexdump( 4, mFrame.head(), 16 );
if ( thisM )
{
if ( mFrameGood )
{
if ( thisM ) {
if ( mFrameGood ) {
Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
mFrameProcessed.setValueImmediate( false );
mFrameReady.updateValueSignal( true );
if ( !mFrameProcessed.getValueImmediate() )
{
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;
@ -347,45 +310,34 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
return( false );
}
mFrameCount++;
}
else
{
} else {
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
}
mFrame.clear();
}
}
else
{
if ( mFrame.size() )
{
} else {
if ( mFrame.size() ) {
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
}
else
{
} else {
Warning( "Discarding frame %d", mFrameCount );
}
mFrameGood = false;
mFrame.clear();
}
if ( thisM )
{
if ( thisM ) {
mFrameGood = true;
prevM = true;
}
else
} else
prevM = false;
updateJitter( rtpHeader );
return( true );
return true;
}
bool RtpSource::getFrame( Buffer &buffer )
{
bool RtpSource::getFrame( Buffer &buffer ) {
Debug( 3, "Getting frame" );
if ( !mFrameReady.getValueImmediate() )
{
if ( !mFrameReady.getValueImmediate() ) {
// Allow for a couple of spurious returns
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
if ( count > 1 )
@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer )
mFrameReady.setValueImmediate( false );
mFrameProcessed.updateValueSignal( true );
Debug( 4, "Copied %d bytes", buffer.size() );
return( true );
return true;
}
#endif // HAVE_LIBAVCODEC

View File

@ -46,53 +46,54 @@ bool RtspThread::sendCommand( std::string message ) {
message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq );
Debug( 2, "Sending RTSP message: %s", message.c_str() );
if ( mMethod == RTP_RTSP_HTTP ) {
message = base64Encode( message );
Debug( 2, "Sending encoded RTSP message: %s", message.c_str() );
if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
return( false );
message = base64Encode(message);
Debug(2, "Sending encoded RTSP message: %s", message.c_str());
if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) {
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return false;
}
} else {
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
return( false );
if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) {
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return false;
}
}
return( true );
return true;
}
bool RtspThread::recvResponse( std::string &response ) {
if ( mRtspSocket.recv( response ) < 0 )
Error( "Recv failed; %s", strerror(errno) );
Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() );
Error("Recv failed; %s", strerror(errno));
Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size());
float respVer = 0;
respCode = -1;
char respText[ZM_NETWORK_BUFSIZ];
if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) {
if ( sscanf(response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) {
if ( isalnum(response[0]) ) {
Error( "Response parse failure in '%s'", response.c_str() );
Error("Response parse failure in '%s'", response.c_str());
} else {
Error( "Response parse failure, %zd bytes follow", response.size() );
Error("Response parse failure, %zd bytes follow", response.size());
if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
}
return( false );
return false;
}
if ( respCode == 401) {
if ( respCode == 401 ) {
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
mAuthenticator->checkAuthResponse(response);
mNeedAuth = true;
return( false );
return false;
} else if ( respCode != 200 ) {
Error( "Unexpected response code %d, text is '%s'", respCode, respText );
return( false );
Error("Unexpected response code %d, text is '%s'", respCode, respText);
return false;
}
return( true );
}
return true;
} // end RtspThread::recResponse
int RtspThread::requestPorts() {
if ( !smMinDataPort ) {
char sql[ZM_SQL_SML_BUFSIZ];
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -107,7 +108,7 @@ int RtspThread::requestPorts() {
int nMonitors = mysql_num_rows( result );
int position = 0;
if ( nMonitors ) {
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 id = atoi(dbrow[0]);
if ( mId == id ) {
position = i;
@ -126,22 +127,30 @@ int RtspThread::requestPorts() {
Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort );
}
for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) {
PortSet::const_iterator iter = smAssignedPorts.find( i );
PortSet::const_iterator iter = smAssignedPorts.find(i);
if ( iter == smAssignedPorts.end() ) {
smAssignedPorts.insert( i );
return( i );
smAssignedPorts.insert(i);
return i;
}
}
Panic( "Can assign RTP port, no ports left in pool" );
return( -1 );
Panic("Can assign RTP port, no ports left in pool");
return -1;
}
void RtspThread::releasePorts( int port ) {
if ( port > 0 )
smAssignedPorts.erase( port );
smAssignedPorts.erase(port);
}
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) :
RtspThread::RtspThread(
int id,
RtspMethod method,
const std::string &protocol,
const std::string &host,
const std::string &port,
const std::string &path,
const std::string &auth,
bool rtsp_describe) :
mId( id ),
mMethod( method ),
mProtocol( protocol ),
@ -168,10 +177,10 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol,
mSsrc = rand();
Debug( 2, "RTSP Local SSRC is %x", mSsrc );
Debug(2, "RTSP Local SSRC is %x", mSsrc);
if ( mMethod == RTP_RTSP_HTTP )
mHttpSession = stringtf( "%d", rand() );
mHttpSession = stringtf("%d", rand());
mNeedAuth = false;
StringVector parts = split(auth,":");
@ -216,8 +225,8 @@ int RtspThread::run() {
bool authTried = false;
if ( mMethod == RTP_RTSP_HTTP ) {
if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) )
Fatal( "Unable to connect auxiliary RTSP/HTTP socket" );
if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) )
Fatal("Unable to connect auxiliary RTSP/HTTP socket");
//Select select( 0.25 );
//select.addReader( &mRtspSocket2 );
//while ( select.wait() )
@ -240,15 +249,15 @@ int RtspThread::run() {
message += "\r\n";
Debug( 2, "Sending HTTP message: %s", message.c_str() );
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
return( -1 );
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return -1;
}
if ( mRtspSocket.recv( response ) < 0 ) {
Error( "Recv failed; %s", strerror(errno) );
return( -1 );
Error("Recv failed; %s", strerror(errno));
return -1;
}
Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() );
Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size());
float respVer = 0;
respCode = -1;
if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) {
@ -259,25 +268,25 @@ int RtspThread::run() {
if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
}
return( -1 );
return -1;
}
// If Server requests authentication, check WWW-Authenticate header and fill required fields
// for requested authentication method
if (respCode == 401 && !authTried) {
if ( respCode == 401 && !authTried ) {
mNeedAuth = true;
mAuthenticator->checkAuthResponse(response);
Debug(2, "Processed 401 response");
mRtspSocket.close();
if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) )
Fatal( "Unable to reconnect RTSP socket" );
if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) )
Fatal("Unable to reconnect RTSP socket");
Debug(2, "connection should be reopened now");
}
} while (respCode == 401 && !authTried);
if ( respCode != 200 ) {
Error( "Unexpected response code %d, text is '%s'", respCode, respText );
return( -1 );
Error("Unexpected response code %d, text is '%s'", respCode, respText);
return -1;
}
message = "POST "+mPath+" HTTP/1.0\r\n";
@ -300,25 +309,25 @@ int RtspThread::run() {
// Request supported RTSP commands by the server
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
if ( !sendCommand( message ) )
return( -1 );
return -1;
// A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry.
if ( !recvResponse( response ) ) {
if ( !recvResponse(response) ) {
if ( mNeedAuth ) {
Debug( 2, "Resending OPTIONS due to possible auth requirement" );
if ( !sendCommand( message ) )
return( -1 );
if ( !recvResponse( response ) )
return( -1 );
if ( !sendCommand(message) )
return -1;
if ( !recvResponse(response) )
return -1;
} else {
return( -1 );
return -1;
}
} // end if failed response maybe due to auth
char publicLine[256] = "";
StringVector lines = split( response, "\r\n" );
StringVector lines = split(response, "\r\n");
for ( size_t i = 0; i < lines.size(); i++ )
sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine );
sscanf(lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine);
// Check if the server supports the GET_PARAMETER command
// If yes, it is likely that the server will request this command as a keepalive message
@ -331,44 +340,45 @@ int RtspThread::run() {
do {
if (mNeedAuth)
authTried = true;
sendCommand( message );
sleep( 1 );
res = recvResponse( response );
if (!res && respCode==401)
sendCommand(message);
// FIXME WHy sleep 1?
sleep(1);
res = recvResponse(response);
if ( !res && respCode==401 )
mNeedAuth = true;
} while (!res && respCode==401 && !authTried);
const std::string endOfHeaders = "\r\n\r\n";
size_t sdpStart = response.find( endOfHeaders );
if( sdpStart == std::string::npos )
return( -1 );
size_t sdpStart = response.find(endOfHeaders);
if ( sdpStart == std::string::npos )
return -1;
if ( mRtspDescribe ) {
std::string DescHeader = response.substr( 0,sdpStart );
Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() );
std::string DescHeader = response.substr(0, sdpStart);
Debug(1, "Processing DESCRIBE response header '%s'", DescHeader.c_str());
lines = split( DescHeader, "\r\n" );
lines = split(DescHeader, "\r\n");
for ( size_t i = 0; i < lines.size(); i++ ) {
// If the device sends us a url value for Content-Base in the response header, we should use that instead
if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) {
mUrl = trimSpaces( lines[i].substr( 13 ) );
Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
break;
}
// If the device sends us a url value for Content-Base in the response header, we should use that instead
if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) {
mUrl = trimSpaces( lines[i].substr( 13 ) );
Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
break;
}
}
} // end foreach line
} // end if mRtspDescribe
sdpStart += endOfHeaders.length();
std::string sdp = response.substr( sdpStart );
Debug( 1, "Processing SDP '%s'", sdp.c_str() );
std::string sdp = response.substr(sdpStart);
Debug(1, "Processing SDP '%s'", sdp.c_str());
try {
mSessDesc = new SessionDescriptor( mUrl, sdp );
mFormatContext = mSessDesc->generateFormatContext();
} catch( const Exception &e ) {
Error( e.getMessage().c_str() );
return( -1 );
return -1;
}
#if 0
@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
Hexdump( 4, (char *)buffer, 16 );
rtpDataThread.recvPacket( buffer+4, len );
Debug( 4, "Received" );
}
else if ( channel == remoteChannels[1] )
{
} else if ( channel == remoteChannels[1] ) {
// len = ntohs( *((unsigned short *)(buffer+2)) );
// Debug( 4, "Got %d bytes on control channel %d", nBytes, channel );
Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
Hexdump( 4, (char *)buffer, 16 );
rtpCtrlThread.recvPackets( buffer+4, len );
}
else
{
} else {
Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
buffer.clear();
break;
}
buffer.consume( len+4 );
nBytes -= len+4;
}
else
{
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
{
} else {
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
Debug( 4, "Got keepalive response '%s'", (char *)buffer );
//buffer.consume( keepaliveResponse.size() );
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
{
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
int discardBytes = charPtr-(char *)buffer;
buffer -= discardBytes;
}
else
{
} else {
buffer.clear();
}
}
else
{
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
{
} else {
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
int discardBytes = charPtr-(char *)buffer;
Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
Hexdump( -1, (char *)buffer, discardBytes );
buffer -= discardBytes;
}
else
{
} else {
Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
Hexdump( -1, (char *)buffer, 32 );
buffer.clear();
@ -764,16 +759,14 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
rtpDataThread.start();
rtpCtrlThread.start();
while( !mStop )
{
while ( !mStop ) {
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) )
{
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) {
if ( !sendCommand( message ) )
return( -1 );
return -1;
lastKeepalive = time(NULL);
}
usleep( 100000 );
usleep(100000);
}
#if 0
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
@ -783,10 +776,10 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
return( -1 );
#endif
message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
if ( !sendCommand( message ) )
return( -1 );
if ( !recvResponse( response ) )
return( -1 );
if ( !sendCommand(message) )
return -1;
if ( !recvResponse(response) )
return -1;
rtpDataThread.stop();
rtpCtrlThread.stop();
@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
break;
}
default:
{
Panic( "Got unexpected method %d", mMethod );
Panic("Got unexpected method %d", mMethod);
break;
}
}
return( 0 );
return 0;
}
#endif // HAVE_LIBAVFORMAT

View File

@ -26,17 +26,17 @@
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
@ -45,36 +45,36 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
};
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
};
#else
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
@ -83,7 +83,7 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
@ -105,7 +105,7 @@ SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
mTtl( 16 ),
mNoAddresses( 0 )
{
StringVector tokens = split( connInfo, " " );
StringVector tokens = split(connInfo, " ");
if ( tokens.size() < 3 )
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
mNetworkType = tokens[0];
@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
mValue = atoi(tokens[1].c_str());
}
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
SessionDescriptor::MediaDescriptor::MediaDescriptor(
const std::string &type,
int port,
int numPorts,
const std::string &transport,
int payloadType ) :
mType( type ),
mPort( port ),
mNumPorts( numPorts ),
@ -164,14 +169,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
if ( line.empty() )
break;
Debug( 3, "Processing SDP line '%s'", line.c_str() );
Debug(3, "Processing SDP line '%s'", line.c_str());
const char sdpType = line[0];
if ( line[1] != '=' )
throw Exception( "Invalid SDP format at '"+line+"'" );
throw Exception("Invalid SDP format at '"+line+"'");
line.erase( 0, 2 );
switch( sdpType )
{
line.erase(0, 2);
switch( sdpType ) {
case 'v' :
mVersion = line;
break;
@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
mAttributes.push_back( line );
StringVector tokens = split( line, ":", 2 );
std::string attrName = tokens[0];
if ( currMedia )
{
if ( attrName == "control" )
{
if ( currMedia ) {
if ( attrName == "control" ) {
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
currMedia->setControlUrl( tokens[1] );
}
else if ( attrName == "range" )
{
}
else if ( attrName == "rtpmap" )
{
} else if ( attrName == "range" ) {
} else if ( attrName == "rtpmap" ) {
// a=rtpmap:96 MP4V-ES/90000
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
@ -226,53 +224,46 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
std::string payloadDesc = attrTokens[1];
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{
if ( attrTokens.size() > 1 ) {
StringVector payloadTokens = split( attrTokens[1], "/" );
std::string payloadDesc = payloadTokens[0];
int payloadClock = atoi(payloadTokens[1].c_str());
currMedia->setPayloadDesc( payloadDesc );
currMedia->setClock( payloadClock );
}
}
else if ( attrName == "framesize" )
{
} else if ( attrName == "framesize" ) {
// a=framesize:96 320-240
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " " );
throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split(tokens[1], " ");
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'",
currMedia->getPayloadType(), payloadType, line.c_str()));
//currMedia->setPayloadType( payloadType );
StringVector sizeTokens = split( attrTokens[1], "-" );
StringVector sizeTokens = split(attrTokens[1], "-");
int width = atoi(sizeTokens[0].c_str());
int height = atoi(sizeTokens[1].c_str());
currMedia->setFrameSize( width, height );
}
else if ( attrName == "framerate" )
{
currMedia->setFrameSize(width, height);
} else if ( attrName == "framerate" ) {
// a=framerate:5.0
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'");
double frameRate = atof(tokens[1].c_str());
currMedia->setFrameRate( frameRate );
}
else if ( attrName == "fmtp" )
{
currMedia->setFrameRate(frameRate);
} else if ( attrName == "fmtp" ) {
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " ", 2 );
throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split(tokens[1], " ", 2);
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'",
currMedia->getPayloadType(), payloadType, line.c_str()));
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{
if ( attrTokens.size() > 1 ) {
StringVector attr2Tokens = split( attrTokens[1], "; " );
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
{
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) {
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
if ( attr3Tokens[0] == "profile-level-id" ) {
@ -292,40 +283,39 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
} else if ( attrName == "mpeg4-esid" ) {
// a=mpeg4-esid:201
} else {
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str());
}
} else {
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
Debug(3, "Ignoring general SDP attribute '%s'", line.c_str());
}
break;
}
case 'm' :
{
StringVector tokens = split( line, " " );
StringVector tokens = split(line, " ");
if ( tokens.size() < 4 )
throw Exception( "Can't parse SDP media description '"+line+"'" );
throw Exception("Can't parse SDP media description '"+line+"'");
std::string mediaType = tokens[0];
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
StringVector portTokens = split( tokens[1], "/" );
throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'");
StringVector portTokens = split(tokens[1], "/");
int mediaPort = atoi(portTokens[0].c_str());
int mediaNumPorts = 1;
if ( portTokens.size() > 1 )
mediaNumPorts = atoi(portTokens[1].c_str());
std::string mediaTransport = tokens[2];
if ( mediaTransport != "RTP/AVP" )
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
throw Exception("Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'");
int payloadType = atoi(tokens[3].c_str());
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
mMediaList.push_back( currMedia );
currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType);
mMediaList.push_back(currMedia);
break;
}
}
}
} // end switch
} // end foreach line
}
SessionDescriptor::~SessionDescriptor()
{
SessionDescriptor::~SessionDescriptor() {
if ( mConnInfo )
delete mConnInfo;
if ( mBandInfo )
@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor()
delete mMediaList[i];
}
AVFormatContext *SessionDescriptor::generateFormatContext() const
{
AVFormatContext *SessionDescriptor::generateFormatContext() const {
AVFormatContext *formatContext = avformat_alloc_context();
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
@ -353,35 +342,40 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
for ( unsigned int i = 0; i < mMediaList.size(); i++ ) {
const MediaDescriptor *mediaDesc = mMediaList[i];
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
AVStream *stream = av_new_stream( formatContext, i );
AVStream *stream = av_new_stream(formatContext, i);
#else
AVStream *stream = avformat_new_stream( formatContext, NULL );
AVStream *stream = avformat_new_stream(formatContext, NULL);
stream->id = i;
#endif
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
AVCodecContext *codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_context, stream->codecpar);
stream->codec = codec_context;
#else
AVCodecContext *codec_context = stream->codec;
#endif
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
std::string type = mediaDesc->getType();
Debug(1, "Looking for codec for %s payload type %d / %s",
type.c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str());
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mediaDesc->getType() == "video" )
if ( type == "video" )
codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" )
else if ( type == "audio" )
codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" )
else if ( type == "application" )
codec_context->codec_type = AVMEDIA_TYPE_DATA;
#else
if ( mediaDesc->getType() == "video" )
if ( type == "video" )
codec_context->codec_type = CODEC_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" )
else if ( type == "audio" )
codec_context->codec_type = CODEC_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" )
else if ( type == "application" )
codec_context->codec_type = CODEC_TYPE_DATA;
#endif
else
Warning("Unknown media_type %s", type.c_str());
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
std::string codec_name;
@ -392,9 +386,9 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) {
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName );
codec_name = std::string(smStaticPayloads[i].payloadName);
#else
strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name));
#endif
codec_context->codec_type = smStaticPayloads[i].codecType;
codec_context->codec_id = smStaticPayloads[i].codecId;
@ -406,11 +400,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
// Look in dynamic table
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) {
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) {
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
Debug(1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName);
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName );
codec_name = std::string(smStaticPayloads[i].payloadName);
#else
strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name));
#endif
codec_context->codec_type = smDynamicPayloads[i].codecType;
codec_context->codec_id = smDynamicPayloads[i].codecId;
@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
break;
}
}
}
} /// end if static or dynamic
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
if ( codec_name.empty() )
@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if ( !stream->codec->codec_name[0] )
#endif
{
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
Warning( "Can't find payload details for %s payload type %d, name %s",
mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
//return( 0 );
}
if ( mediaDesc->getWidth() )
@ -449,11 +444,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
while (*value && *value != ','
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
*dst++ = *value++;
*dst++ = *value++;
}
*dst++ = '\0';
if (*value == ',')
if ( *value == ',' )
value++;
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
@ -468,23 +463,23 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
FF_INPUT_BUFFER_PADDING_SIZE
#endif
);
if(dest) {
if(codec_context->extradata_size) {
// av_realloc?
if ( dest ) {
if ( codec_context->extradata_size ) {
// av_realloc?
memcpy(dest, codec_context->extradata, codec_context->extradata_size);
av_free(codec_context->extradata);
}
av_free(codec_context->extradata);
}
memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+codec_context->extradata_size+sizeof(start_sequence)+
packet_size, 0,
memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+codec_context->extradata_size+sizeof(start_sequence)+
packet_size, 0,
#if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
AV_INPUT_BUFFER_PADDING_SIZE
AV_INPUT_BUFFER_PADDING_SIZE
#else
FF_INPUT_BUFFER_PADDING_SIZE
FF_INPUT_BUFFER_PADDING_SIZE
#endif
);
);
codec_context->extradata= dest;
codec_context->extradata_size+= sizeof(start_sequence)+packet_size;
@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
}
}
return( formatContext );
return formatContext;
}
#endif // HAVE_LIBAVFORMAT

View File

@ -103,7 +103,6 @@ VideoStore::VideoStore(
#endif
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
video_out_stream = avformat_new_stream(oc, NULL);
if ( !video_out_stream ) {
Error("Unable to create video out stream");
@ -112,6 +111,7 @@ VideoStore::VideoStore(
Debug(2, "Success creating video out stream");
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
// by allocating our own copy, we don't run into the problems when we free the streams
video_out_ctx = avcodec_alloc_context3(video_out_codec);
// Since we are not re-encoding, all we have to do is copy the parameters
@ -122,21 +122,9 @@ VideoStore::VideoStore(
return;
}
#else
video_out_stream = avformat_new_stream(oc, NULL);
if ( !video_out_stream ) {
Error("Unable to create video out stream");
return;
} else {
Debug(2, "Success creating video out stream");
}
video_out_ctx = video_out_stream->codec;
// This will wipe out the codec defaults
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
//video_out_ctx->width = video_in_ctx->width;
//video_out_ctx->height = video_in_ctx->height;
//video_out_ctx->pix_fmt = video_in_ctx->pix_fmt;
//video_out_ctx->max_b_frames = video_in_ctx->max_b_frames;
//video_out_ctx->has_b_frames = video_in_ctx->has_b_frames;
if ( ret < 0 ) {
Fatal("Unable to copy in video ctx to out video ctx %s",
av_make_error_string(ret).c_str());
@ -173,7 +161,6 @@ VideoStore::VideoStore(
break;
}
if ( !video_out_ctx->codec_tag ) {
Debug(2, "No codec_tag");
if (
@ -207,6 +194,7 @@ VideoStore::VideoStore(
video_out_stream->r_frame_rate = video_in_stream->r_frame_rate;
}
#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0)
#if 0
if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) {
//video_out_ctx->level = 32;I//
video_out_ctx->bit_rate = 400*1024;
@ -218,6 +206,7 @@ VideoStore::VideoStore(
Debug(2, "Not setting priv_data");
}
}
#endif
ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx);
if ( ret < 0 ) {
Error("Could not initialize video_out_ctx parameters");
@ -1016,7 +1005,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
zm_dump_frame(in_frame, "In frame from decode");
if ( ! resample_audio() ) {
if ( !resample_audio() ) {
//av_frame_unref(in_frame);
return 0;
}
@ -1146,11 +1135,22 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
}
} // end if encoding or copying
opkt.pos = -1;
opkt.stream_index = audio_out_stream->index;
opkt.flags = ipkt->flags;
if ( opkt.dts > opkt.pts ) {
if ( opkt.dts < audio_out_stream->cur_dts ) {
Warning("non increasing dts, fixing");
opkt.dts = audio_out_stream->cur_dts;
if ( opkt.dts > opkt.pts ) {
Debug(1,
"opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 ")."
"Decompression must happen before presentation.",
opkt.dts, opkt.pts);
opkt.pts = opkt.dts;
}
} else if ( opkt.dts > opkt.pts ) {
Debug(1,
"opkt.dts(%" PRId64 ") must be <= opkt.pts(%" PRId64 ")."
"Decompression must happen before presentation.",
@ -1172,7 +1172,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
} // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt)
int VideoStore::resample_audio() {
// Resample the in into the audioSampleBuffer until we process the whole
// Resample the in_frame into the audioSampleBuffer until we process the whole
// decoded data. Note: pts does not survive resampling or converting
#if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE)
#if defined(HAVE_LIBSWRESAMPLE)
@ -1192,8 +1192,8 @@ int VideoStore::resample_audio() {
}
/** Store the new samples in the FIFO buffer. */
ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples);
if ( ret < in_frame->nb_samples ) {
Error("Could not write data to FIFO on %d written", ret);
if ( ret < out_frame->nb_samples ) {
Error("Could not write data to FIFO on %d written, expecting %d", ret, out_frame->nb_samples);
return 0;
}

View File

@ -1,5 +1,5 @@
<?php
error_reporting(0);
error_reporting(E_ERROR);
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
ajaxError('No event id(s) supplied');
@ -136,7 +136,7 @@ if ( canEdit('Events') ) {
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>false));
break;
case 'delete' :
$Event = new Event($_REQUEST['id']);
$Event = new ZM\Event($_REQUEST['id']);
if ( ! $Event->Id() ) {
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true, 'message'=> 'Event not found.'));
} else {

View File

@ -288,9 +288,12 @@ class EventsController extends AppController {
$this->Event->recursive = -1;
$results = array();
$this->FilterComponent = $this->Components->load('Filter');
$conditions = $this->FilterComponent->buildFilter($conditions);
if ( $this->request->params['named'] ) {
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
} else {
$conditions = array();
}
array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)"));
$query = $this->Event->find('all', array(
'fields' => array(
'MonitorId',

View File

@ -294,7 +294,7 @@ $SLANG = array(
'Display' => 'Prikaz',
'Displaying' => 'Prikazujem',
'DonateAlready' => 'Ne, već sam napravio donaciju.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'Donate' => 'Molimo donirajte',
'DonateRemindDay' => 'Ne još, podsjetime za 1 dan',
'DonateRemindHour' => 'Ne još, podsjetime za 1 sat',

View File

@ -293,7 +293,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => '请捐款',
'DonateAlready' => '不,我已经捐赠过了',
'DonateEnticement' => '迄今您已经运行ZoneMinder有一阵子了希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是并将保持免费和开源该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能那么请考虑为该项目捐款。捐款不是必须的任何数量的捐赠我们都很感谢。<br/><br/>如果您愿意捐款,请选择下列选项,或者访问 http://www.zoneminder.com/donate.html 捐赠主页。<br/><br/>感谢您使用ZoneMinder并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议这可以提升您的ZoneMinder的体验。',
'DonateEnticement' => '迄今您已经运行ZoneMinder有一阵子了希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是并将保持免费和开源该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能那么请考虑为该项目捐款。捐款不是必须的任何数量的捐赠我们都很感谢。<br/><br/>如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。<br/><br/>感谢您使用ZoneMinder并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议这可以提升您的ZoneMinder的体验。',
'DonateRemindDay' => '现在不1天内再次提醒我',
'DonateRemindHour' => '现在不1小时内再次提醒我',
'DonateRemindMonth' => '现在不1个月内再次提醒我',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Prosím podpořte',
'DonateAlready' => 'Ne, už jsem podpořil',
'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.<br><br>Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte http://www.zoneminder.com/donate.html.<br><br>Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.',
'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.<br><br>Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte https://zoneminder.com/donate/.<br><br>Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.',
'DonateRemindDay' => 'Nyní ne, připomenout za 1 den',
'DonateRemindHour' => 'Nyní ne, připomenout za hodinu',
'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc',

View File

@ -291,7 +291,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Bitte spenden Sie.',
'DonateAlready' => 'Nein, ich habe schon gespendet',
'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.<br><br>Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse http://www.zoneminder.com/donate.html oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.<br><br>Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!',
'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.<br><br>Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse https://zoneminder.com/donate/ oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.<br><br>Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!',
'DonateRemindDay' => 'Noch nicht, erinnere mich in einem Tag noch mal.',
'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.',
'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Venligst Donér',
'DonateAlready' => 'Nej, jeg har allerede doneret',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag',
'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time',
'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned',

View File

@ -296,7 +296,7 @@ $SLANG = array(
'Display' => 'Display',
'Displaying' => 'Displaying',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'Donate' => 'Please Donate',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',

View File

@ -240,7 +240,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Por favor, done',
'DonateAlready' => 'No, ya he donado',
'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.<br/><br/>Si desea hacer una donación por favor seleccione la opción de debajo o vaya a http://www.zoneminder.com/donate.html en su navegador.<br/><br/>Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.',
'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.<br/><br/>Si desea hacer una donación por favor seleccione la opción de debajo o vaya a https://zoneminder.com/donate/ en su navegador.<br/><br/>Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.',
'DonateRemindDay' => 'Aún no, recordarme de nuevo en 1 día',
'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora',
'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes',

View File

@ -296,7 +296,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Palun Anneta',
'DonateAlready' => 'EI, Ma olen juba annetanud',
'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Ei veel, tuleta meelde ühe päeva pärast',
'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast',
'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast',

View File

@ -295,7 +295,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Réaliser détection native',
'Donate' => 'Veuillez faire un don',
'DonateAlready' => 'Non, j\'ai déjà donné',
'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.<br><br>Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur http://www.zoneminder.com/donate.html à l\'aide de votre navigateur internet.<br><br>Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.',
'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.<br><br>Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur https://zoneminder.com/donate/ à l\'aide de votre navigateur internet.<br><br>Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.',
'DonateRemindDay' => 'Pas encore, me rappeler dans 1 jour',
'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure',
'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'úøåí áá÷ùä',
'DonateAlready' => 'ìà, úøîúé ëáø',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'òãééï ìà, äæëø ìà áòåã éåí àçã',
'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú',
'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã',

View File

@ -332,7 +332,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Kérem támogasson',
'DonateAlready' => 'Nem, én már támogattam',
'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.<br><br>Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a http://www.zoneminder.com/donate.html oldalt.<br><br>Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.',
'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.<br><br>Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a https://zoneminder.com/donate/ oldalt.<br><br>Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.',
'DonateRemindDay' => 'Nem most, figyelmeztessen egy nap múlva',
'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva',
'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva',

View File

@ -294,7 +294,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Donate,per favore',
'DonateAlready' => 'No, ho gia donato... ',
'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.<br><br>Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a http://www.zoneminder.com/donate.html .<br><br>Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.',
'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.<br><br>Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a https://zoneminder.com/donate/ .<br><br>Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.',
'DonateRemindDay' => 'Non ancora, ricordamelo ancora tra 1 giorno',
'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora',
'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Geef a.u.b. een donatie',
'DonateAlready' => 'Nee, ik heb al gedoneerd',
'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd. <br><br> Als u wilt doneren geef dat hieronder dan aan of ga naar http://www.zoneminder.com/donate.html in uw browser.<br><br>Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.',
'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd. <br><br> Als u wilt doneren geef dat hieronder dan aan of ga naar https://zoneminder.com/donate/ in uw browser.<br><br>Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.',
'DonateRemindDay' => 'Nu niet, herinner mij over 1 dag hieraan',
'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan',
'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan',

View File

@ -304,7 +304,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -229,7 +229,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -260,7 +260,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -20,6 +20,7 @@
// ZoneMinder Russian Translation by Borodin A.S.
// ZoneMinder Russian Translation updated by IDDQDesnik, 2017
// ZoneMinder Russian Translation updated by santos995, 2019
// Notes for Translators
// 0. Get some credit, put your name in the line above (optional)
@ -76,11 +77,11 @@ $SLANG = array(
'32BitColour' => '32 битный цвет', // Added - 2011-06-15
'8BitGrey' => '256 оттенков серого',
'Action' => 'Действие',
'Actual' => 'Actual', // Added - 2018-08-30
'Actual' => 'Актуальный', // Edited - 2019-03-25
'AddNewControl' => 'Добавить новый',
'AddNewMonitor' => 'Добавить монитор',
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
'AddNewServer' => 'Добавить новый сервер', // Edited - 2019-03-25
'AddNewStorage' => 'Добавить новое хранилище', // Edited - 2019-03-25
'AddNewUser' => 'Добавить пользователя',
'AddNewZone' => 'Добавить зону',
'Alarm' => 'Тревога',
@ -110,24 +111,24 @@ $SLANG = array(
'AttrCause' => 'Причина',
'AttrDiskBlocks' => 'Диск, блоки',
'AttrDiskPercent' => 'Диск, проценты',
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
'AttrDiskSpace' => 'Дисковое пространство', // Edited - 2019-03-24
'AttrDuration' => 'Длительность',
'AttrEndDate' => 'End Date', // Added - 2018-08-30
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
'AttrEndTime' => 'End Time', // Added - 2018-08-30
'AttrEndDate' => 'Дата окончания', // Edited - 2019-03-24
'AttrEndDateTime' => 'Дата/время окончания', // Edited - 2019-03-24
'AttrEndTime' => 'Время окончания', // Edited - 2019-03-24
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
'AttrFilterServer' => 'Фильтр для серверов запущен', // Edited - 2019-03-24
'AttrFrames' => 'Кол-во кадров',
'AttrId' => 'ИД',
'AttrMaxScore' => 'Макс. оценка',
'AttrMonitorId' => 'ИД Монитора',
'AttrMonitorName' => 'Название Монитора',
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
'AttrMonitorServer' => 'Монитор серверов запущен', // Edited - 2019-03-24
'AttrName' => 'Имя',
'AttrNotes' => 'Примечание',
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
'AttrStartDate' => 'Дата начала', // Edited - 2019-03-24
'AttrStartDateTime' => 'Дата/Время начала', // Edited - 2019-03-24
'AttrStartTime' => 'Время начала', // Edited - 2019-03-24
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
'AttrStateId' => 'Run State', // Added - 2018-08-30
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
@ -170,7 +171,7 @@ $SLANG = array(
'BadStreamReplayBuffer'=> 'Буфер потока повторного воспроизведения должен быть целочисленным и большим либо равным нулю',
'BadWarmupCount' => 'Кол-во кадров разогрева должно быть целочисленным и большим либо равным нулю',
'BadWebColour' => 'Цвет отметки должен быть правильным Web-цветом',
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
'BadWebSitePath' => 'Пожалуйста, введите полной название сайта url, включая http:// или https:// префикс.', // Edited - 2019-03-24
'BadWidth' => 'Неправильная ширина',
'Bandwidth' => 'канал',
'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing;
@ -178,9 +179,9 @@ $SLANG = array(
'BlobSizes' => 'Размер объектов',
'Blobs' => 'Кол-во объектов',
'Brightness' => 'Яркость',
'Buffer' => 'Buffer', // Added - 2015-04-18
'Buffer' => 'Буфер', // Edited - 2019-03-24
'Buffers' => 'Буферы',
'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18
'CSSDescription' => 'Изменить стандартный CSS для данного компьютера', // Edited - 2019-03-24
'CanAutoFocus' => 'Автофокус',
'CanAutoGain' => 'Автоусиление',
'CanAutoIris' => 'Автодиафрагма',
@ -206,7 +207,7 @@ $SLANG = array(
'CanMoveRel' => 'Относительное перемещение',
'CanPan' => 'Панорама' ,
'CanReset' => 'Сброс',
'CanReboot' => 'Can Reboot',
'CanReboot' => 'Перезагрузка', // Added - 2019-03-24
'CanSetPresets' => 'Создание предустановок',
'CanSleep' => 'Сон',
'CanTilt' => 'Наклон',
@ -225,17 +226,17 @@ $SLANG = array(
'CaptureHeight' => 'Размер по Y',
'CaptureMethod' => 'Метод захвата', // Added - 2009-02-08
'CapturePalette' => 'Режим захвата',
'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18
'CaptureResolution' => 'Разрешение', // Edited - 2019-03-24
'CaptureWidth' => 'Размер по X',
'Cause' => 'Причина',
'CheckMethod' => 'Метод проверки тревоги',
'ChooseDetectedCamera' => 'Выберите камеру', // Added - 2009-03-31
'ChooseFilter' => 'Выбрать фильтр',
'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17
'ChooseLogFormat' => 'Выбрать формат лога', // Edited - 2019-03-25
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
'ChoosePreset' => 'Выберите предустановку',
'Clear' => 'Очистить', // Added - 2011-06-16
'CloneMonitor' => 'Clone', // Added - 2018-08-30
'CloneMonitor' => 'Клонировать', // Edited - 2019-03-25
'Close' => 'Закрыть',
'Colour' => 'Цвет',
'Command' => 'Command',
@ -249,7 +250,7 @@ $SLANG = array(
'ConjOr' => 'или',
'Console' => 'Сервер',
'ContactAdmin' => 'Пожалуйста обратитесь к вашему администратору.',
'Continue' => 'Continue',
'Continue' => 'Продолжить', // Added - 2019-03-25
'Contrast' => 'Контраст',
'Control' => 'Управление',
'ControlAddress' => 'Адрес устройства',
@ -263,12 +264,12 @@ $SLANG = array(
'CycleWatch' => 'Циклический просмотр',
'DateTime' => 'Дата/Время', // Added - 2011-06-16
'Day' => 'День',
'Debug' => 'Debug',
'Debug' => 'Отладка', // Added - 2019-03-25
'DefaultRate' => 'Скорость по умолчанию',
'DefaultScale' => 'Масштаб по умолчанию',
'DefaultView' => 'Вид по умолчанию',
'Deinterlacing' => 'Устранение чересстрочности', // Added - 2015-04-18
'Delay' => 'Delay', // Added - 2015-04-18
'Delay' => 'Задержка', // Edited - 2019-03-25
'Delete' => 'Удалить',
'DeleteAndNext' => 'Удалить &amp; след.',
'DeleteAndPrev' => 'Удалить &amp; пред.',
@ -276,18 +277,18 @@ $SLANG = array(
'Description' => 'Описание',
'DetectedCameras' => 'Найденные камеры', // Added - 2009-03-31
'DetectedProfiles' => 'Найденные профили', // Added - 2015-04-18
'Device' => 'Device', // Added - 2009-02-08
'Device' => 'Устройство', // Edited - 2019-03-25
'DeviceChannel' => 'Канал',
'DeviceFormat' => 'Формат',
'DeviceNumber' => 'Номер устройства',
'DevicePath' => 'Путь к устройству',
'Devices' => 'Devices',
'Devices' => 'Устройства', // Edited - 2019-03-25
'Dimensions' => 'Размеры',
'DisableAlarms' => 'Запретить тревогу',
'Disk' => 'Диск',
'Display' => 'Display', // Added - 2011-01-30
'Displaying' => 'Отображено', // Added - 2011-06-16
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'DoNativeMotionDetection'=> 'Использовать встроенное обнаружение движения', // Edited - 2019-03-25
'Donate' => 'Поддержите проект',
'DonateAlready' => 'Нет, я уже сделал пожертвование',
'DonateEnticement' => 'Вы какое-то время используете ZoneMinder и, надеемся, находите его полезным дополнением к вашей домашней или рабочей безопасности. Хотя ZoneMinder есть и будет оставаться свободным и бесплатным, он требует денег на разработку и поддержку. Если Вы хотите поддержать его будущее развитие и новые функции, пожалуйста сделайте пожертвование. Это, конечно, необязательно, но очень высоко ценится. Вы можете пожертвовать любую сумму.<br><br>Если Вы хотите сделать пожертвование, то выберите соответствующий вариант ниже или перейдите по адресу https://www.bountysource.com/teams/zoneminder в вашем браузере.<br><br>Спасибо за использование ZoneMinder, и не забывайте посетить форум на ZoneMinder.com для поддержки и пожеланий как сделать ZoneMinder еще лучше.',
@ -298,11 +299,11 @@ $SLANG = array(
'DonateRemindWeek' => 'Нет, не сейчас, напомнить через неделю',
'DonateYes' => 'Да, я хотел бы сделать пожертвование',
'Download' => 'Скачать',
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
'DownloadVideo' => 'Скачать видео', // Added - 2019-03-24
'DuplicateMonitorName' => 'Скопировать имя монитора', // Added - 2019-03-25
'Duration' => 'Длительность',
'Edit' => 'Редактирование',
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
'EditLayout' => 'Редактирование шаблона', // Added - 2019-03-25
'Email' => 'Email',
'EnableAlarms' => 'Разрешить тревогу',
'Enabled' => 'Включен',
@ -313,13 +314,13 @@ $SLANG = array(
'Etc' => 'и т.д.',
'Event' => 'Событие',
'EventFilter' => 'Фильтр событий',
'EventId' => 'Event Id',
'EventName' => 'Event Name',
'EventId' => 'Id события', // Added - 2019-03-25
'EventName' => 'Имя события', // Added - 2019-03-25
'EventPrefix' => 'Префикс события',
'Events' => 'События',
'Exclude' => 'Исключить',
'Execute' => 'Выполнить',
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
'Exif' => 'Включить EXIF информацию в изображение', // Added - 2019-03-24
'Export' => 'Экспорт',
'ExportDetails' => 'Экспортировать описание события',
'ExportFailed' => 'Ошибка экспорта',
@ -351,14 +352,14 @@ $SLANG = array(
'FilterMessageEvents' => 'Message details of all matches',
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
'FilterPx' => 'Пкс фильтра',
'FilterUnset' => 'You must specify a filter width and height',
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
'FilterUploadEvents' => 'Upload all matches',
'FilterVideoEvents' => 'Create video for all matches',
'FilterUnset' => 'Вы должны указать ширину и высоту фильтра', // Added - 2019-03-25
'FilterUpdateDiskSpace'=> 'Обновить используемое дисковое пространство', // Edited - 2019-03-25
'FilterUploadEvents' => 'Загрузить все совпадения', // Added - 2019-03-25
'FilterVideoEvents' => 'Создать видео для всех совпадений', // Added - 2019-03-25
'Filters' => 'Фильтры',
'First' => 'Первый',
'FlippedHori' => 'Flipped Horizontally',
'FlippedVert' => 'Flipped Vertically',
'FlippedHori' => 'Перевернутый горизонтально', // Added - 2019-03-25
'FlippedVert' => 'Перевернутый вертикально', // Added - 2019-03-25
'FnMocord' => 'Mocord', // Added 2013.08.16.
'FnModect' => 'Modect', // Added 2013.08.16.
'FnMonitor' => 'Monitor', // Added 2013.08.16.
@ -377,7 +378,7 @@ $SLANG = array(
'Function' => 'Функция',
'Gain' => 'Gain',
'General' => 'Основные',
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
'GenerateDownload' => 'Сгенерировать загрузку', // Edited - 2019-03-25
'GenerateVideo' => 'Генерировать видео',
'GeneratingVideo' => 'Генерируется видео',
'GoToZoneMinder' => 'Перейти на ZoneMinder.com',
@ -398,7 +399,7 @@ $SLANG = array(
'High' => 'широкий',
'HighBW' => 'Широкий канал',
'Home' => 'Домой',
'Hostname' => 'Hostname', // Added - 2018-08-30
'Hostname' => 'Имя хоста', // Edited - 2019-03-25
'Hour' => 'Час',
'Hue' => 'Оттенок',
'Id' => 'ИД',
@ -406,13 +407,13 @@ $SLANG = array(
'Ignore' => 'Игнорировать',
'Image' => 'Изображение',
'ImageBufferSize' => 'Буфер изображений',
'Images' => 'Images',
'Images' => 'Изображения', // Added - 2019-03-25
'In' => 'In',
'Include' => 'Включить',
'Inverted' => 'Инвертировать',
'Iris' => 'Наклон',
'KeyString' => 'Key String',
'Label' => 'Label',
'Label' => 'Имя', // Added - 2019-03-25
'Language' => 'Язык',
'Last' => 'Последний',
'Layout' => 'Раскладка', // Added - 2009-02-08
@ -423,16 +424,16 @@ $SLANG = array(
'Line' => 'Строка', // Added - 2011-06-16
'LinkedMonitors' => 'Привязанные мониторы',
'List' => 'Список',
'ListMatches' => 'List Matches', // Added - 2018-08-30
'ListMatches' => 'Список совпадений', // Edited - 2019-03-25
'Load' => 'Нагрузка',
'Local' => 'Локальный',
'Log' => 'Лог', // Added - 2011-06-16;
'LoggedInAs' => 'Пользователь',
'Logging' => 'Logging', // Added - 2011-06-16
'Logging' => 'Логгирование', // Added - 2019-03-24
'LoggingIn' => 'Вход в систему',
'Login' => 'Войти',
'Logout' => 'Выйти',
'Logs' => 'Logs', // Added - 2011-06-17
'Logs' => 'Логи', // Added - 2019-03-24
'Low' => 'узкий',
'LowBW' => 'Узкий канал',
'Main' => 'Основные',
@ -440,7 +441,7 @@ $SLANG = array(
'Manual' => 'Manual',
'Mark' => 'Метка',
'Max' => 'Макс.',
'MaxBandwidth' => 'Max Bandwidth',
'MaxBandwidth' => 'Макс. пропускная способность', // Added - 2019-03-25
'MaxBrScore' => 'Макс.<br/>оценка',
'MaxFocusRange' => 'Макс. диап. фокуса',
'MaxFocusSpeed' => 'Макс. скор. фокуса',
@ -510,7 +511,7 @@ $SLANG = array(
'MonitorProbeIntro' => 'В этом списке показаны найденные аналоговые и сетевые камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2009-03-31
'Monitors' => 'Мониторы',
'Montage' => 'Монтаж',
'MontageReview' => 'Montage Review', // Added - 2018-08-30
'MontageReview' => 'Обзор монтажа', // Added - 2019-03-24
'Month' => 'Месяц',
'More' => 'Еще', // Added - 2011-06-16
'MotionFrameSkip' => 'Кол-во пропуск. кадров движения',
@ -530,16 +531,16 @@ $SLANG = array(
'Network' => 'Сеть',
'New' => 'Нов.',
'NewGroup' => 'Новая группа',
'NewLabel' => 'New Label',
'NewLabel' => 'Новое имя', // Added - 2019-03-25
'NewPassword' => 'Новый пароль',
'NewState' => 'Новое состояние',
'NewUser' => 'Новый пользователь',
'Next' => 'След.',
'No' => 'Нет',
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
'NoDetectedCameras' => 'Нет камер', // Added - 2019-03-24
'NoDetectedProfiles' => 'Нет профилей', // Added - 2019-03-24
'NoFramesRecorded' => 'Это событие не содержит кадров',
'NoGroup' => 'No Group',
'NoGroup' => 'Нет группы', // Added - 2019-03-24
'NoSavedFilters' => 'нет сохраненных фильтров',
'NoStatisticsRecorded' => 'Статистика по этому событию/кадру не записана',
'None' => 'отсутствует',
@ -547,8 +548,8 @@ $SLANG = array(
'Normal' => 'Нормальная',
'Notes' => 'Примечание',
'NumPresets' => 'Кол-во предустановок',
'Off' => 'Off',
'On' => 'On',
'Off' => 'Выкл.', // Added - 2019-03-25
'On' => 'Вкл.', // Added - 2019-03-25
'OnvifCredentialsIntro'=> 'Пожалуйста укажите имя пользователя и пароль для выбранной камеры.<br/><br/>Если пользователь для камеры не был создан, тогда будет создан новый с указанными данными.<br/><br/>', // Added - 2015-04-18
'OnvifProbe' => 'ONVIF', // Added - 2015-04-18
'OnvifProbeIntro' => 'В этом списке показаны найденные ONVIF камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
@ -567,7 +568,7 @@ $SLANG = array(
'Open' => 'Открыть',
'OptionHelp' => 'Справка',
'OptionRestartWarning' => 'Эти изменения подействуют только после перезапуска программы.',
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
'OptionalEncoderParam' => 'Необязательные параметры кодировщика', // Added - 2019-03-24
'Options' => 'Опции',
'OrEnterNewName' => 'или введите новое имя',
'Order' => 'Сортировка',
@ -592,7 +593,7 @@ $SLANG = array(
'Play' => 'Играть',
'PlayAll' => 'Воспр. все',
'PleaseWait' => 'Пожалуйста подождите',
'Plugins' => 'Plugins',
'Plugins' => 'Плагины', // Edited - 2019-03-24
'Point' => 'Точка',
'PostEventImageBuffer' => 'Буфер после события',
'PreEventImageBuffer' => 'Буфер до события',
@ -605,13 +606,13 @@ $SLANG = array(
'ProfileProbeIntro' => 'В этом списке показаны существующие профили потока выбранной камеры.<br/><br/>Выберите нужный из списка ниже.<br/><br/>Обратите внимание, что ZoneMinder не может добавить дополнительный профиль, и что выбор профиля может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
'Progress' => 'Прогресс', // Added - 2015-04-18
'Protocol' => 'Протокол',
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
'RTSPDescribe' => 'Использовать RTSP URL для ответа', // Edited - 2019-03-25
'RTSPTransport' => 'Транспортный протокол RTSP', // Edited - 2019-03-25
'Rate' => 'Скорость',
'Real' => 'Реальная',
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
'Record' => 'Record',
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
'RecordAudio' => 'Сохранять ли аудиопоток при сохранении события.', // Edited - 2019-03-25
'RefImageBlendPct' => 'Смешение опорного кадра, %',
'Refresh' => 'Обновить',
'Remote' => 'Удаленный',
@ -627,7 +628,7 @@ $SLANG = array(
'ReplayAll' => 'Все события',
'ReplayGapless' => 'События подряд',
'ReplaySingle' => 'Одно событие',
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
'ReportEventAudit' => 'Отчёт о событиях аудита', // Edited - 2019-03-24
'Reset' => 'Сбросить',
'ResetEventCounts' => 'Обнулить счетчик событий',
'Restart' => 'Перезапустить',
@ -639,14 +640,14 @@ $SLANG = array(
'Rewind' => 'Назад',
'RotateLeft' => 'Повернуть влево',
'RotateRight' => 'Повернуть вправо',
'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25
'RunLocalUpdate' => 'Запустите zmupdate.pl для обновления', // Edited - 2019-03-24
'RunMode' => 'Режим работы',
'RunState' => 'Состояние',
'Running' => 'Выполняется',
'Save' => 'Сохранить',
'SaveAs' => 'Сохранить как',
'SaveFilter' => 'Сохранить фильтр',
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
'SaveJPEGs' => 'Сохранить JPEG-и', // Edited - 2019-03-24
'Scale' => 'Масштаб',
'Score' => 'Оценка',
'Secs' => 'Сек.',
@ -654,46 +655,46 @@ $SLANG = array(
'Select' => 'Выбор',
'SelectFormat' => 'Выберите формат', // Added - 2011-06-17
'SelectLog' => 'Выберите лог', // Added - 2011-06-17
'SelectMonitors' => 'Select Monitors',
'SelectMonitors' => 'Выбрать Мониторы', // Edited - 2019-03-24
'SelfIntersecting' => 'Polygon edges must not intersect',
'Set' => 'Set',
'Set' => 'Установка', // Edited - 2019-03-24
'SetNewBandwidth' => 'Установка новой ширина канала',
'SetPreset' => 'Set Preset',
'SetPreset' => 'Установка пресета', // Edited - 2019-03-24
'Settings' => 'Настройки',
'ShowFilterWindow' => 'Показать окно фильтра',
'ShowTimeline' => 'Показать график',
'SignalCheckColour' => 'Цвет проверки сигнала',
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
'Size' => 'Size',
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
'Size' => 'Размер', // Edited - 2019-03-24
'SkinDescription' => 'Смена стандартного скина для данного компьютера', // Edited - 2019-03-24
'Sleep' => 'Sleep',
'SortAsc' => 'По возр.',
'SortBy' => 'Сортировать',
'SortDesc' => 'По убыв.',
'Source' => 'Источник',
'SourceColours' => 'Source Colours', // Added - 2009-02-08
'SourceColours' => 'Цвета источника', // Edited - 2019-03-24
'SourcePath' => 'Путь к источнику', // Added - 2009-02-08
'SourceType' => 'Тип источника',
'Speed' => 'Speed',
'SpeedHigh' => 'High Speed',
'SpeedLow' => 'Low Speed',
'SpeedMedium' => 'Medium Speed',
'SpeedTurbo' => 'Turbo Speed',
'Speed' => 'Скорость', //Edited - 2019-03-24
'SpeedHigh' => 'Высокая скорость', //Edited - 2019-03-24
'SpeedLow' => 'Низкая скорость', //Edited - 2019-03-24
'SpeedMedium' => 'Средняя скорость',
'SpeedTurbo' => 'Максимальная скорость', // Edited - 2019-03-24
'Start' => 'Запустить',
'State' => 'Состояние',
'Stats' => 'Статистика',
'Status' => 'Статус',
'StatusConnected' => 'Capturing', // Added - 2018-08-30
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
'StatusConnected' => 'Записывается', // Edited - 2019-03-25
'StatusNotRunning' => 'Не запущен', // Edited - 2019-03-25
'StatusRunning' => 'Не записывается', // Edited - 2019-03-25
'StatusUnknown' => 'Неизвестно', // Edited - 2019-03-25
'Step' => 'Шаг',
'StepBack' => 'Кадр назад',
'StepForward' => 'Кадр вперед',
'StepLarge' => 'Large Step',
'StepMedium' => 'Medium Step',
'StepNone' => 'No Step',
'StepSmall' => 'Small Step',
'StepLarge' => 'Большой шаг', // Added - 2019-03-25
'StepMedium' => 'Средний шаг', // Added - 2019-03-25
'StepNone' => 'Без шагов', // Added - 2019-03-25
'StepSmall' => 'Малый шаг', // Added - 2019-03-25
'Stills' => 'Стоп-кадры',
'Stop' => 'Остановить',
'Stopped' => 'Остановлен',
@ -758,24 +759,24 @@ $SLANG = array(
'VersionRemindNever' => 'Не говорить о новых версиях',
'VersionRemindWeek' => 'Напомнить через неделю',
'Video' => 'Видео',
'VideoFormat' => 'Video Format',
'VideoFormat' => 'Формат видео', // Edited - 2019-03-24
'VideoGenFailed' => 'Ошибка генерации видео!',
'VideoGenFiles' => 'Existing Video Files',
'VideoGenNoFiles' => 'No Video Files Found',
'VideoGenNoFiles' => 'Видео не найдено', // Edited - 2019-03-24
'VideoGenParms' => 'Параметры генерации видео',
'VideoGenSucceeded' => 'Video Generation Succeeded!',
'VideoGenSucceeded' => 'Видео сгенерировано!', // Edited - 2019-03-24
'VideoSize' => 'Размер изображения',
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
'View' => 'Просмотр',
'ViewAll' => 'Просм. все',
'ViewEvent' => 'View Event',
'ViewEvent' => 'Просм. событие', // Edited - 2019-03-24
'ViewPaged' => 'Просм. постранично',
'Wake' => 'Wake',
'WarmupFrames' => 'Кадры разогрева',
'Watch' => 'Watch',
'Web' => 'Интерфейс',
'WebColour' => 'Цвет отметки',
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
'WebSiteUrl' => 'URL сайта', // Edited - 2019-03-25
'Week' => 'Неделя',
'White' => 'Бал. белого',
'WhiteBalance' => 'White Balance',
@ -798,7 +799,7 @@ $SLANG = array(
'ZoneMinMaxBlobs' => 'Мин/Макс кол-во объектов',
'ZoneMinMaxFiltArea' => 'Мин/Макс разм. фильтр. зоны ',
'ZoneMinMaxPixelThres' => 'Мин/Макс порог изм. пикс. (0-255)',
'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17
'ZoneMinderLog' => 'Лог ZoneMinder', // Edited - 2019-03-25
'ZoneOverloadFrames' => 'Кол-во игнор. кадров перегрузки',
'Zones' => 'Зоны',
'Zoom' => 'Увеличение',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Var vänlig och donera',
'DonateAlready' => 'Nej, Jag har redan donerat',
'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.<br><br>Om du vill ge ett bidrag väljer du nedan eller surfar till http://www.zoneminder.com/donate.html.<br><br>Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.',
'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.<br><br>Om du vill ge ett bidrag väljer du nedan eller surfar till https://zoneminder.com/donate/.<br><br>Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.',
'DonateRemindDay' => 'Inte än, påminn om 1 dag',
'DonateRemindHour' => 'Inte än, påminn om en 1 timme',
'DonateRemindMonth' => 'Inte än, påminn om 1 månad',

View File

@ -115,7 +115,7 @@
}
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .centerBtn {
background: url("../skins/classic/graphics/graphics/center.png") no-repeat 0 0;
background: url("../skins/classic/graphics/center.png") no-repeat 0 0;
}
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .rightBtn {

View File

@ -173,12 +173,12 @@ echo output_link_if_exists( array(
<script src="<?php echo cache_bust('skins/classic/js/base.js') ?>"></script>
<?php } ?>
<script src="<?php echo cache_bust($skinJsFile) ?>"></script>
<script src="js/logger.js"></script>
<script src="<?php echo cache_bust('js/logger.js')?>"></script>
<?php
if ($basename == 'watch' or $basename == 'log' ) {
// This is used in the log popup for the export function. Not sure if it's used anywhere else
?>
<script src="js/overlay.js"></script>
<script src="<?php echo cache_bust('js/overlay.js') ?>"></script>
<?php } ?>
<?php
if ( $viewJsFile ) {

View File

@ -64,6 +64,15 @@ function initPage() {
return false;
};
});
}
window.addEventListener( 'DOMContentLoaded', initPage );
// Disable form submit on enter
$j('#contentForm input').on('keyup keypress', function(e) {
var keyCode = e.keyCode || e.which;
if (keyCode === 13) {
e.preventDefault();
return false;
}
});
} // end function initPage()
window.addEventListener('DOMContentLoaded', initPage);

View File

@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) )
else
$scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE);
$Event = new Event($event['Id']);
$Event = new ZM\Event($event['Id']);
$eventPath = $Event->Path();
$videoFormats = array();