Merge branch 'master' of github.com:zoneminder/ZoneMinder
This commit is contained in:
commit
81cd338545
|
@ -41,6 +41,7 @@ env:
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
|
||||||
|
- SMPFLAGS=-j4 OS=ubuntu DIST=focal DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
|
||||||
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
|
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
|
||||||
|
|
|
@ -19,6 +19,10 @@ if [ "$1" = "configure" ]; then
|
||||||
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
|
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
|
||||||
a2enmod cgi
|
a2enmod cgi
|
||||||
fi
|
fi
|
||||||
|
if [ ! -e "/etc/apache2/mods-enabled/rewrite.load" ] && [ "$(command -v a2enmod)" != "" ]; then
|
||||||
|
echo "The rewrite module is not enabled in apache2. I am enabling it using a2enmod rewrite."
|
||||||
|
a2enmod rewrite
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
if [ "$ZM_DB_HOST" = "localhost" ]; then
|
||||||
|
|
||||||
|
@ -61,11 +65,10 @@ if [ "$1" = "configure" ]; then
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
# This creates the user.
|
# This creates the user.
|
||||||
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
else
|
fi
|
||||||
echo "Updating permissions"
|
echo "Updating permissions"
|
||||||
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
fi
|
|
||||||
|
|
||||||
zmupdate.pl --nointeractive
|
zmupdate.pl --nointeractive
|
||||||
zmupdate.pl --nointeractive -f
|
zmupdate.pl --nointeractive -f
|
||||||
|
|
|
@ -46,7 +46,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,javascript-common
|
,javascript-common
|
||||||
,libmp4v2-2, libx264-155
|
,libmp4v2-2, libx264-155
|
||||||
,libswscale5
|
,libswscale5
|
||||||
,libswresample4
|
,libswresample3
|
||||||
,ffmpeg
|
,ffmpeg
|
||||||
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
||||||
,libdbd-mysql-perl
|
,libdbd-mysql-perl
|
||||||
|
|
|
@ -61,11 +61,10 @@ if [ "$1" = "configure" ]; then
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
# This creates the user.
|
# This creates the user.
|
||||||
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo "CREATE USER '${ZM_DB_USER}'@localhost IDENTIFIED BY '${ZM_DB_PASS}';" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
else
|
fi
|
||||||
echo "Updating permissions"
|
echo "Updating permissions"
|
||||||
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
fi
|
|
||||||
|
|
||||||
zmupdate.pl --nointeractive
|
zmupdate.pl --nointeractive
|
||||||
zmupdate.pl --nointeractive -f
|
zmupdate.pl --nointeractive -f
|
||||||
|
|
|
@ -69,9 +69,9 @@ sub sendCmd {
|
||||||
|
|
||||||
my $result = undef;
|
my $result = undef;
|
||||||
|
|
||||||
printMsg($cmd, 'Tx');
|
$self->printMsg($cmd, 'Tx');
|
||||||
|
|
||||||
my $req = HTTP::Request->new( POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi" );
|
my $req = HTTP::Request->new(POST=>"http://$self->{Monitor}->{ControlAddress}/$cgi.cgi");
|
||||||
$req->content($cmd);
|
$req->content($cmd);
|
||||||
my $res = $self->{ua}->request($req);
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
|
|
@ -185,16 +185,12 @@ sub discover {
|
||||||
push @responses, $response;
|
push @responses, $response;
|
||||||
};
|
};
|
||||||
|
|
||||||
## try both soap versions
|
|
||||||
|
|
||||||
my $uuid_gen = Data::UUID->new();
|
my $uuid_gen = Data::UUID->new();
|
||||||
|
|
||||||
if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) {
|
if ( ( ! $soap_version ) or ( $soap_version eq '1.1' ) ) {
|
||||||
my %services;
|
my %services;
|
||||||
|
|
||||||
if ( $verbose ) {
|
print "Probing for SOAP 1.1\n" if $verbose;
|
||||||
print "Probing for SOAP 1.1\n";
|
|
||||||
}
|
|
||||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||||
# no_dispatch => '1',
|
# no_dispatch => '1',
|
||||||
});
|
});
|
||||||
|
@ -218,7 +214,7 @@ sub discover {
|
||||||
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
|
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
print $result . "\n" if $verbose;
|
print $result."\n" if $verbose;
|
||||||
|
|
||||||
push @results, interpret_messages($svc_discover, \%services, @responses);
|
push @results, interpret_messages($svc_discover, \%services, @responses);
|
||||||
@responses = ();
|
@responses = ();
|
||||||
|
@ -226,9 +222,7 @@ sub discover {
|
||||||
|
|
||||||
if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) {
|
if ( ( ! $soap_version ) or ( $soap_version eq '1.2' ) ) {
|
||||||
my %services;
|
my %services;
|
||||||
if ( $verbose ) {
|
print "Probing for SOAP 1.2\n" if $verbose;
|
||||||
print "Probing for SOAP 1.2\n";
|
|
||||||
}
|
|
||||||
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({
|
||||||
# no_dispatch => '1',
|
# no_dispatch => '1',
|
||||||
});
|
});
|
||||||
|
@ -256,7 +250,7 @@ sub discover {
|
||||||
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
|
To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
print $result . "\n" if $verbose;
|
print $result."\n" if $verbose;
|
||||||
push @results, interpret_messages($svc_discover, \%services, @responses);
|
push @results, interpret_messages($svc_discover, \%services, @responses);
|
||||||
} # end if doing soap 1.2
|
} # end if doing soap 1.2
|
||||||
return @results;
|
return @results;
|
||||||
|
@ -281,19 +275,16 @@ sub profiles {
|
||||||
}
|
}
|
||||||
my @profiles;
|
my @profiles;
|
||||||
|
|
||||||
|
|
||||||
foreach my $profile ( @{ $result->get_Profiles() } ) {
|
foreach my $profile ( @{ $result->get_Profiles() } ) {
|
||||||
|
my $token = $profile->attr()->get_token() ;
|
||||||
|
my $Name = $profile->get_Name();
|
||||||
|
|
||||||
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
my $VideoEncoderConfiguration = $profile->get_VideoEncoderConfiguration();
|
||||||
if ( ! $VideoEncoderConfiguration ) {
|
if ( ! $VideoEncoderConfiguration ) {
|
||||||
print "Unknown profile $token $Name.\n";
|
print "No VideoEncoderConfiguration in profile $token $Name.\n";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $token = $profile->attr()->get_token() ;
|
|
||||||
my $Name = $profile->get_Name();
|
|
||||||
|
|
||||||
# Specification gives conflicting values for unicast stream types, try both.
|
# Specification gives conflicting values for unicast stream types, try both.
|
||||||
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
# http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri
|
||||||
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
|
foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast', 'RTP-multicast', 'RTP-Multicast' ) {
|
||||||
|
@ -315,15 +306,15 @@ sub profiles {
|
||||||
my $Width = $Resolution ? $Resolution->get_Width() : 0;
|
my $Width = $Resolution ? $Resolution->get_Width() : 0;
|
||||||
my $Height = $Resolution ? $Resolution->get_Height() : 0;
|
my $Height = $Resolution ? $Resolution->get_Height() : 0;
|
||||||
|
|
||||||
push @profiles, {
|
push @profiles, [
|
||||||
print join(', ', $token,
|
$token,
|
||||||
$Name,
|
$Name,
|
||||||
$VideoEncoderConfiguration->get_Encoding(),
|
$VideoEncoderConfiguration->get_Encoding(),
|
||||||
$Width,
|
$Width,
|
||||||
$Height,
|
$Height,
|
||||||
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
|
$VideoEncoderConfiguration->get_RateControl()->get_FrameRateLimit(),
|
||||||
$Uri,
|
$Uri,
|
||||||
) . "\n";
|
];
|
||||||
} # end foreach streamtype
|
} # end foreach streamtype
|
||||||
|
|
||||||
} # end foreach profile
|
} # end foreach profile
|
||||||
|
|
1
src/zm.h
1
src/zm.h
|
@ -22,6 +22,7 @@
|
||||||
#define ZM_H
|
#define ZM_H
|
||||||
|
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
#ifdef SOLARIS
|
#ifdef SOLARIS
|
||||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||||
#include <string.h> // define strerror() and friends
|
#include <string.h> // define strerror() and friends
|
||||||
|
|
|
@ -468,7 +468,7 @@ void av_packet_rescale_ts(
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_video_stream( AVStream * stream ) {
|
bool is_video_stream(const AVStream * stream) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
if ( stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
if ( stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
||||||
#else
|
#else
|
||||||
|
@ -480,10 +480,14 @@ bool is_video_stream( AVStream * stream ) {
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
Debug(2, "Not a video type %d != %d", stream->codecpar->codec_type, AVMEDIA_TYPE_VIDEO);
|
||||||
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_video_context( AVCodecContext *codec_context ) {
|
bool is_video_context(const AVCodecContext *codec_context ) {
|
||||||
return
|
return
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
( codec_context->codec_type == AVMEDIA_TYPE_VIDEO );
|
( codec_context->codec_type == AVMEDIA_TYPE_VIDEO );
|
||||||
|
@ -492,7 +496,7 @@ bool is_video_context( AVCodecContext *codec_context ) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_audio_stream( AVStream * stream ) {
|
bool is_audio_stream(const AVStream * stream ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
if ( stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ) {
|
if ( stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ) {
|
||||||
#else
|
#else
|
||||||
|
@ -507,7 +511,7 @@ bool is_audio_stream( AVStream * stream ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_audio_context( AVCodecContext *codec_context ) {
|
bool is_audio_context(const AVCodecContext *codec_context ) {
|
||||||
return
|
return
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
( codec_context->codec_type == AVMEDIA_TYPE_AUDIO );
|
( codec_context->codec_type == AVMEDIA_TYPE_AUDIO );
|
||||||
|
|
|
@ -372,10 +372,10 @@ void zm_dump_codecpar(const AVCodecParameters *par);
|
||||||
|
|
||||||
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
|
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
|
||||||
|
|
||||||
bool is_video_stream(AVStream *);
|
bool is_video_stream(const AVStream *);
|
||||||
bool is_audio_stream(AVStream *);
|
bool is_audio_stream(const AVStream *);
|
||||||
bool is_video_context(AVCodec *);
|
bool is_video_context(const AVCodec *);
|
||||||
bool is_audio_context(AVCodec *);
|
bool is_audio_context(const AVCodec *);
|
||||||
|
|
||||||
int zm_receive_packet(AVCodecContext *context, AVPacket &packet);
|
int zm_receive_packet(AVCodecContext *context, AVPacket &packet);
|
||||||
|
|
||||||
|
|
|
@ -151,11 +151,13 @@ int RemoteCameraRtsp::Disconnect() {
|
||||||
|
|
||||||
int RemoteCameraRtsp::PrimeCapture() {
|
int RemoteCameraRtsp::PrimeCapture() {
|
||||||
Debug(2, "Waiting for sources");
|
Debug(2, "Waiting for sources");
|
||||||
for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) {
|
for ( int i = 0; (i < 100) && !rtspThread->hasSources(); i++ ) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
if ( !rtspThread->hasSources() )
|
if ( !rtspThread->hasSources() ) {
|
||||||
Fatal("No RTSP sources");
|
Error("No RTSP sources");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Debug(2, "Got sources");
|
Debug(2, "Got sources");
|
||||||
|
|
||||||
|
@ -167,38 +169,21 @@ int RemoteCameraRtsp::PrimeCapture() {
|
||||||
|
|
||||||
// Find the first video stream.
|
// Find the first video stream.
|
||||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) {
|
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
if ( is_video_stream(mFormatContext->streams[i]) ) {
|
||||||
AVMediaType codec_type = mFormatContext->streams[i]->codecpar->codec_type;
|
|
||||||
#else
|
|
||||||
AVMediaType codec_type = mFormatContext->streams[i]->codec->codec_type;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
|
||||||
if ( codec_type == AVMEDIA_TYPE_VIDEO )
|
|
||||||
#else
|
|
||||||
if ( codec_type == CODEC_TYPE_VIDEO )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if ( mVideoStreamId == -1 ) {
|
if ( mVideoStreamId == -1 ) {
|
||||||
mVideoStreamId = i;
|
mVideoStreamId = i;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Have another video stream.");
|
Debug(2, "Have another video stream.");
|
||||||
}
|
}
|
||||||
} else
|
} else if ( is_audio_stream(mFormatContext->streams[i]) ) {
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
|
||||||
if ( codec_type == AVMEDIA_TYPE_AUDIO )
|
|
||||||
#else
|
|
||||||
if ( codec_type == CODEC_TYPE_AUDIO )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if ( mAudioStreamId == -1 ) {
|
if ( mAudioStreamId == -1 ) {
|
||||||
mAudioStreamId = i;
|
mAudioStreamId = i;
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Have another audio stream.");
|
Debug(2, "Have another audio stream.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Have unknown codec type in stream %d : %d", i, codec_type);
|
Debug(1, "Have unknown codec type in stream %d", i);
|
||||||
}
|
}
|
||||||
} // end foreach stream
|
} // end foreach stream
|
||||||
|
|
||||||
|
@ -288,7 +273,6 @@ int RemoteCameraRtsp::Capture( Image &image ) {
|
||||||
|
|
||||||
if ( rtspThread->getFrame(buffer) ) {
|
if ( rtspThread->getFrame(buffer) ) {
|
||||||
Debug(3, "Read frame %d bytes", buffer.size());
|
Debug(3, "Read frame %d bytes", buffer.size());
|
||||||
Debug(4, "Address %p", buffer.head());
|
|
||||||
Hexdump(4, buffer.head(), 16);
|
Hexdump(4, buffer.head(), 16);
|
||||||
|
|
||||||
if ( !buffer.size() )
|
if ( !buffer.size() )
|
||||||
|
@ -310,14 +294,15 @@ int RemoteCameraRtsp::Capture( Image &image ) {
|
||||||
// IDR
|
// IDR
|
||||||
buffer += lastSps;
|
buffer += lastSps;
|
||||||
buffer += lastPps;
|
buffer += lastPps;
|
||||||
|
} else {
|
||||||
|
Debug(2, "Unknown nalType %d", nalType);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Not an h264 packet");
|
Debug(3, "Not an h264 packet");
|
||||||
}
|
}
|
||||||
|
|
||||||
av_init_packet(&packet);
|
av_init_packet(&packet);
|
||||||
|
while ( (!frameComplete) && (buffer.size() > 0) ) {
|
||||||
while ( !frameComplete && (buffer.size() > 0) ) {
|
|
||||||
packet.data = buffer.head();
|
packet.data = buffer.head();
|
||||||
packet.size = buffer.size();
|
packet.size = buffer.size();
|
||||||
bytes += packet.size;
|
bytes += packet.size;
|
||||||
|
@ -330,6 +315,7 @@ int RemoteCameraRtsp::Capture( Image &image ) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
frameComplete = true;
|
||||||
Debug(2, "Frame: %d - %d/%d", frameCount, len, buffer.size());
|
Debug(2, "Frame: %d - %d/%d", frameCount, len, buffer.size());
|
||||||
//if ( buffer.size() < 400 )
|
//if ( buffer.size() < 400 )
|
||||||
//Hexdump( 0, buffer.head(), buffer.size() );
|
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||||
|
|
|
@ -27,98 +27,81 @@
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
|
RtpDataThread::RtpDataThread(RtspThread &rtspThread, RtpSource &rtpSource) :
|
||||||
|
mRtspThread(rtspThread), mRtpSource(rtpSource), mStop(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
bool RtpDataThread::recvPacket(const unsigned char *packet, size_t packetLen) {
|
||||||
{
|
|
||||||
const RtpDataHeader *rtpHeader;
|
const RtpDataHeader *rtpHeader;
|
||||||
rtpHeader = (RtpDataHeader *)packet;
|
rtpHeader = (RtpDataHeader *)packet;
|
||||||
|
|
||||||
//printf( "D: " );
|
Debug(5, "Ver: %d P: %d Pt: %d Mk: %d Seq: %d T/S: %x SSRC: %x",
|
||||||
//for ( int i = 0; i < 32; i++ )
|
rtpHeader->version,
|
||||||
//printf( "%02x ", (unsigned char)packet[i] );
|
rtpHeader->p,
|
||||||
//printf( "\n" );
|
rtpHeader->pt,
|
||||||
|
rtpHeader->m,
|
||||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
ntohs(rtpHeader->seqN),
|
||||||
Debug( 5, "P: %d", rtpHeader->p );
|
ntohl(rtpHeader->timestampN),
|
||||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
ntohl(rtpHeader->ssrcN));
|
||||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
|
||||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
|
||||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
|
||||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
|
||||||
|
|
||||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||||
|
|
||||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) {
|
||||||
{
|
Warning("Discarding packet for unrecognised ssrc %lx", ssrc);
|
||||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
return false;
|
||||||
return( false );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
return mRtpSource.handlePacket(packet, packetLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpDataThread::run()
|
int RtpDataThread::run() {
|
||||||
{
|
Debug(2, "Starting data thread %d on port %d",
|
||||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
mRtpSource.getSsrc(), mRtpSource.getLocalDataPort());
|
||||||
|
|
||||||
SockAddrInet localAddr;
|
SockAddrInet localAddr;
|
||||||
UdpInetServer rtpDataSocket;
|
UdpInetServer rtpDataSocket;
|
||||||
if ( mRtpSource.getLocalHost() != "" ) {
|
if ( mRtpSource.getLocalHost() != "" ) {
|
||||||
if ( !rtpDataSocket.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ) )
|
if ( !rtpDataSocket.bind(mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort()) )
|
||||||
Fatal( "Failed to bind RTP server" );
|
Fatal("Failed to bind RTP server");
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
} else {
|
||||||
}
|
if ( !rtpDataSocket.bind(
|
||||||
else
|
mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0",
|
||||||
{
|
mRtpSource.getLocalDataPort() ) )
|
||||||
if ( !rtpDataSocket.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalDataPort() ) )
|
Fatal("Failed to bind RTP server");
|
||||||
Fatal( "Failed to bind RTP server" );
|
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
|
||||||
}
|
}
|
||||||
|
Debug(3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort());
|
||||||
|
|
||||||
Select select( 3 );
|
Select select(3);
|
||||||
select.addReader( &rtpDataSocket );
|
select.addReader(&rtpDataSocket);
|
||||||
|
|
||||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||||
while ( !mStop && select.wait() >= 0 )
|
while ( !zm_terminate && !mStop && (select.wait() >= 0) ) {
|
||||||
{
|
|
||||||
if ( mStop )
|
|
||||||
break;
|
|
||||||
Select::CommsList readable = select.getReadable();
|
Select::CommsList readable = select.getReadable();
|
||||||
if ( readable.size() == 0 )
|
if ( readable.size() == 0 ) {
|
||||||
{
|
Error("RTP timed out");
|
||||||
Error( "RTP timed out" );
|
|
||||||
mStop = true;
|
mStop = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter )
|
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
|
||||||
{
|
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) ) {
|
||||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
int nBytes = socket->recv(buffer, sizeof(buffer));
|
||||||
{
|
Debug(4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc());
|
||||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
if ( nBytes ) {
|
||||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
recvPacket(buffer, nBytes);
|
||||||
if ( nBytes )
|
} else {
|
||||||
{
|
|
||||||
recvPacket( buffer, nBytes );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mStop = true;
|
mStop = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Panic("Barfed");
|
||||||
}
|
}
|
||||||
else
|
} // end foreach commsList
|
||||||
{
|
|
||||||
Panic( "Barfed" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rtpDataSocket.close();
|
rtpDataSocket.close();
|
||||||
mRtspThread.stop();
|
mRtspThread.stop();
|
||||||
return( 0 );
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -37,25 +37,25 @@ RtpSource::RtpSource(
|
||||||
uint32_t rtpClock,
|
uint32_t rtpClock,
|
||||||
uint32_t rtpTime,
|
uint32_t rtpTime,
|
||||||
_AVCODECID codecId ) :
|
_AVCODECID codecId ) :
|
||||||
mId( id ),
|
mId(id),
|
||||||
mSsrc( ssrc ),
|
mSsrc(ssrc),
|
||||||
mLocalHost( localHost ),
|
mLocalHost(localHost),
|
||||||
mRemoteHost( remoteHost ),
|
mRemoteHost(remoteHost),
|
||||||
mRtpClock( rtpClock ),
|
mRtpClock(rtpClock),
|
||||||
mCodecId( codecId ),
|
mCodecId(codecId),
|
||||||
mFrame( 65536 ),
|
mFrame(65536),
|
||||||
mFrameCount( 0 ),
|
mFrameCount(0),
|
||||||
mFrameGood( true ),
|
mFrameGood(true),
|
||||||
mFrameReady( false ),
|
mFrameReady(false),
|
||||||
mFrameProcessed( false )
|
mFrameProcessed(false)
|
||||||
{
|
{
|
||||||
char hostname[256] = "";
|
char hostname[256] = "";
|
||||||
gethostname( hostname, sizeof(hostname) );
|
gethostname(hostname, sizeof(hostname));
|
||||||
|
|
||||||
mCname = stringtf( "zm-%d@%s", mId, hostname );
|
mCname = stringtf("zm-%d@%s", mId, hostname);
|
||||||
Debug( 3, "RTP CName = %s", mCname.c_str() );
|
Debug(3, "RTP CName = %s", mCname.c_str());
|
||||||
|
|
||||||
init( seq );
|
init(seq);
|
||||||
mMaxSeq = seq - 1;
|
mMaxSeq = seq - 1;
|
||||||
mProbation = MIN_SEQUENTIAL;
|
mProbation = MIN_SEQUENTIAL;
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ RtpSource::RtpSource(
|
||||||
Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId);
|
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 ) {
|
void RtpSource::init(uint16_t seq) {
|
||||||
Debug(3, "Initialising sequence");
|
Debug(3, "Initialising sequence");
|
||||||
mBaseSeq = seq;
|
mBaseSeq = seq;
|
||||||
mMaxSeq = seq;
|
mMaxSeq = seq;
|
||||||
|
@ -93,36 +93,36 @@ void RtpSource::init( uint16_t seq ) {
|
||||||
mTransit = 0;
|
mTransit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::updateSeq( uint16_t seq ) {
|
bool RtpSource::updateSeq(uint16_t seq) {
|
||||||
uint16_t uDelta = seq - mMaxSeq;
|
uint16_t uDelta = seq - mMaxSeq;
|
||||||
|
|
||||||
// Source is not valid until MIN_SEQUENTIAL packets with
|
// Source is not valid until MIN_SEQUENTIAL packets with
|
||||||
// sequential sequence numbers have been received.
|
// sequential sequence numbers have been received.
|
||||||
Debug( 5, "Seq: %d", seq );
|
Debug(5, "Seq: %d", seq);
|
||||||
|
|
||||||
if ( mProbation) {
|
if ( mProbation) {
|
||||||
// packet is in sequence
|
// packet is in sequence
|
||||||
if ( seq == mMaxSeq + 1) {
|
if ( seq == mMaxSeq + 1 ) {
|
||||||
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
Debug(3, "Sequence in probation %d, in sequence", mProbation);
|
||||||
mProbation--;
|
mProbation--;
|
||||||
mMaxSeq = seq;
|
mMaxSeq = seq;
|
||||||
if ( mProbation == 0 ) {
|
if ( mProbation == 0 ) {
|
||||||
init( seq );
|
init(seq);
|
||||||
mReceivedPackets++;
|
mReceivedPackets++;
|
||||||
return( true );
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
Warning("Sequence in probation %d, out of sequence", mProbation);
|
||||||
mProbation = MIN_SEQUENTIAL - 1;
|
mProbation = MIN_SEQUENTIAL - 1;
|
||||||
mMaxSeq = seq;
|
mMaxSeq = seq;
|
||||||
return( false );
|
return false;
|
||||||
}
|
}
|
||||||
return( true );
|
return true;
|
||||||
} else if ( uDelta < MAX_DROPOUT ) {
|
} else if ( uDelta < MAX_DROPOUT ) {
|
||||||
if ( uDelta == 1 ) {
|
if ( uDelta == 1 ) {
|
||||||
Debug( 4, "Packet in sequence, gap %d", uDelta );
|
Debug(4, "Packet in sequence, gap %d", uDelta);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Packet in sequence, gap %d", uDelta );
|
Warning("Packet in sequence, gap %d", uDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// in order, with permissible gap
|
// in order, with permissible gap
|
||||||
|
@ -132,22 +132,22 @@ bool RtpSource::updateSeq( uint16_t seq ) {
|
||||||
}
|
}
|
||||||
mMaxSeq = seq;
|
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 );
|
Warning("Packet out of sequence, gap %d", uDelta);
|
||||||
// the sequence number made a very large jump
|
// the sequence number made a very large jump
|
||||||
if ( seq == mBadSeq ) {
|
if ( seq == mBadSeq ) {
|
||||||
Debug( 3, "Restarting sequence" );
|
Debug(3, "Restarting sequence");
|
||||||
// Two sequential packets -- assume that the other side
|
// Two sequential packets -- assume that the other side
|
||||||
// restarted without telling us so just re-sync
|
// restarted without telling us so just re-sync
|
||||||
// (i.e., pretend this was the first packet).
|
// (i.e., pretend this was the first packet).
|
||||||
init( seq );
|
init(seq);
|
||||||
} else {
|
} else {
|
||||||
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
||||||
return( false );
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
Warning("Packet duplicate or reordered, gap %d", uDelta);
|
||||||
// duplicate or reordered packet
|
// duplicate or reordered packet
|
||||||
return( false );
|
return false;
|
||||||
}
|
}
|
||||||
mReceivedPackets++;
|
mReceivedPackets++;
|
||||||
return( uDelta==1?true:false );
|
return( uDelta==1?true:false );
|
||||||
|
@ -155,17 +155,22 @@ bool RtpSource::updateSeq( uint16_t seq ) {
|
||||||
|
|
||||||
void RtpSource::updateJitter( const RtpDataHeader *header ) {
|
void RtpSource::updateJitter( const RtpDataHeader *header ) {
|
||||||
if ( mRtpFactor > 0 ) {
|
if ( mRtpFactor > 0 ) {
|
||||||
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t(tvDiffSec(mBaseTimeReal) * mRtpFactor);
|
||||||
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
|
||||||
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
|
||||||
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
|
|
||||||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||||
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
|
||||||
|
Debug(5, "Delta rtp = %.6f\n"
|
||||||
|
"Local RTP time = %x",
|
||||||
|
"Packet RTP time = %x",
|
||||||
|
"Packet transit RTP time = %x",
|
||||||
|
tvDiffSec(mBaseTimeReal),
|
||||||
|
localTimeRtp,
|
||||||
|
ntohl(header->timestampN),
|
||||||
|
packetTransit);
|
||||||
|
|
||||||
if ( mTransit > 0 ) {
|
if ( mTransit > 0 ) {
|
||||||
// Jitter
|
// Jitter
|
||||||
int d = packetTransit - mTransit;
|
int d = packetTransit - mTransit;
|
||||||
Debug( 5, "Jitter D = %d", d );
|
Debug(5, "Jitter D = %d", d);
|
||||||
if ( d < 0 )
|
if ( d < 0 )
|
||||||
d = -d;
|
d = -d;
|
||||||
//mJitter += (1./16.) * ((double)d - mJitter);
|
//mJitter += (1./16.) * ((double)d - mJitter);
|
||||||
|
@ -175,32 +180,33 @@ void RtpSource::updateJitter( const RtpDataHeader *header ) {
|
||||||
} else {
|
} else {
|
||||||
mJitter = 0;
|
mJitter = 0;
|
||||||
}
|
}
|
||||||
Debug( 5, "RTP Jitter: %d", mJitter );
|
Debug(5, "RTP Jitter: %d", mJitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) {
|
void RtpSource::updateRtcpData(
|
||||||
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
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 );
|
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();
|
mBaseTimeReal = tvNow();
|
||||||
mBaseTimeNtp = ntpTime;
|
mBaseTimeNtp = ntpTime;
|
||||||
mBaseTimeRtp = rtpTime;
|
mBaseTimeRtp = rtpTime;
|
||||||
} else if ( !mRtpClock ) {
|
} else if ( !mRtpClock ) {
|
||||||
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
|
Debug(5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x"
|
||||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
"ntpTime: %ld.%06ld, rtpTime: %x",
|
||||||
|
mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime,
|
||||||
|
ntpTime.tv_sec, ntpTime.tv_usec, rtpTime);
|
||||||
|
|
||||||
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
|
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
|
||||||
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
||||||
|
|
||||||
//Debug( 5, "Real-diff: %.6f", diffRealTime );
|
|
||||||
Debug( 5, "NTP-diff: %.6f", diffNtpTime );
|
|
||||||
Debug( 5, "RTP-diff: %d", diffRtpTime );
|
|
||||||
|
|
||||||
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
||||||
|
|
||||||
Debug( 5, "RTPfactor: %d", mRtpFactor );
|
Debug( 5, "NTP-diff: %.6f RTP-diff: %d RTPfactor: %d",
|
||||||
|
diffNtpTime, diffRtpTime, mRtpFactor);
|
||||||
}
|
}
|
||||||
mLastSrTimeNtpSecs = ntpTimeSecs;
|
mLastSrTimeNtpSecs = ntpTimeSecs;
|
||||||
mLastSrTimeNtpFrac = ntpTimeFrac;
|
mLastSrTimeNtpFrac = ntpTimeFrac;
|
||||||
|
@ -211,31 +217,37 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint
|
||||||
void RtpSource::updateRtcpStats() {
|
void RtpSource::updateRtcpStats() {
|
||||||
uint32_t extendedMax = mCycles + mMaxSeq;
|
uint32_t extendedMax = mCycles + mMaxSeq;
|
||||||
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
||||||
|
|
||||||
Debug( 5, "Expected packets = %d", mExpectedPackets );
|
|
||||||
|
|
||||||
// The number of packets lost is defined to be the number of packets
|
// The number of packets lost is defined to be the number of packets
|
||||||
// expected less the number of packets actually received:
|
// expected less the number of packets actually received:
|
||||||
mLostPackets = mExpectedPackets - mReceivedPackets;
|
mLostPackets = mExpectedPackets - mReceivedPackets;
|
||||||
Debug( 5, "Lost packets = %d", mLostPackets );
|
|
||||||
|
|
||||||
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
||||||
Debug( 5, "Expected interval = %d", expectedInterval );
|
|
||||||
mExpectedPrior = mExpectedPackets;
|
mExpectedPrior = mExpectedPackets;
|
||||||
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
||||||
Debug( 5, "Received interval = %d", receivedInterval );
|
|
||||||
mReceivedPrior = mReceivedPackets;
|
mReceivedPrior = mReceivedPackets;
|
||||||
uint32_t lostInterval = expectedInterval - receivedInterval;
|
uint32_t lostInterval = expectedInterval - receivedInterval;
|
||||||
Debug( 5, "Lost interval = %d", lostInterval );
|
|
||||||
|
|
||||||
if ( expectedInterval == 0 || lostInterval <= 0 )
|
if ( expectedInterval == 0 || lostInterval <= 0 )
|
||||||
mLostFraction = 0;
|
mLostFraction = 0;
|
||||||
else
|
else
|
||||||
mLostFraction = (lostInterval << 8) / expectedInterval;
|
mLostFraction = (lostInterval << 8) / expectedInterval;
|
||||||
Debug( 5, "Lost fraction = %d", mLostFraction );
|
|
||||||
|
Debug(5,
|
||||||
|
"Expected packets = %d\n",
|
||||||
|
"Lost packets = %d\n",
|
||||||
|
"Expected interval = %d\n",
|
||||||
|
"Received interval = %d\n",
|
||||||
|
"Lost interval = %d\n",
|
||||||
|
"Lost fraction = %d\n",
|
||||||
|
|
||||||
|
mExpectedPackets,
|
||||||
|
mLostPackets,
|
||||||
|
expectedInterval,
|
||||||
|
receivedInterval,
|
||||||
|
lostInterval,
|
||||||
|
mLostFraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
|
bool RtpSource::handlePacket(const unsigned char *packet, size_t packetLen) {
|
||||||
const RtpDataHeader *rtpHeader;
|
const RtpDataHeader *rtpHeader;
|
||||||
rtpHeader = (RtpDataHeader *)packet;
|
rtpHeader = (RtpDataHeader *)packet;
|
||||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||||
|
@ -248,15 +260,15 @@ 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.
|
// that there is no marker bit by changing the number of bits in the payload type field.
|
||||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||||
|
|
||||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) {
|
if ( updateSeq(ntohs(rtpHeader->seqN)) ) {
|
||||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
Hexdump(4, packet+rtpHeaderSize, 16);
|
||||||
|
|
||||||
if ( mFrameGood ) {
|
if ( mFrameGood ) {
|
||||||
int extraHeader = 0;
|
int extraHeader = 0;
|
||||||
|
|
||||||
if ( mCodecId == AV_CODEC_ID_H264 ) {
|
if ( mCodecId == AV_CODEC_ID_H264 ) {
|
||||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||||
Debug( 3, "Have H264 frame: nal type is %d", nalType );
|
Debug(3, "Have H264 frame: nal type is %d", nalType);
|
||||||
|
|
||||||
switch (nalType) {
|
switch (nalType) {
|
||||||
case 24: // STAP-A
|
case 24: // STAP-A
|
||||||
|
@ -281,45 +293,46 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
|
||||||
extraHeader = 2;
|
extraHeader = 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Debug(3, "Unhandled nalType %d", nalType );
|
Debug(3, "Unhandled nalType %d", nalType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append NAL frame start code
|
// Append NAL frame start code
|
||||||
if ( !mFrame.size() )
|
if ( !mFrame.size() )
|
||||||
mFrame.append( "\x0\x0\x1", 3 );
|
mFrame.append("\x0\x0\x1", 3);
|
||||||
} // end if H264
|
} // end if H264
|
||||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
mFrame.append(packet+rtpHeaderSize+extraHeader,
|
||||||
|
packetLen-rtpHeaderSize-extraHeader);
|
||||||
} else {
|
} else {
|
||||||
Debug( 3, "NOT H264 frame: type is %d", mCodecId );
|
Debug(3, "NOT H264 frame: type is %d", mCodecId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Hexdump( 4, mFrame.head(), 16 );
|
Hexdump(4, mFrame.head(), 16);
|
||||||
|
|
||||||
if ( thisM ) {
|
if ( thisM ) {
|
||||||
if ( mFrameGood ) {
|
if ( mFrameGood ) {
|
||||||
Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
|
Debug(3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size());
|
||||||
|
|
||||||
mFrameProcessed.setValueImmediate( false );
|
mFrameProcessed.setValueImmediate(false);
|
||||||
mFrameReady.updateValueSignal( true );
|
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
|
// 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;
|
// if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false;
|
||||||
|
|
||||||
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
|
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
|
||||||
if( count > 1 )
|
if ( count > 1 )
|
||||||
return( false );
|
return false;
|
||||||
}
|
}
|
||||||
mFrameCount++;
|
mFrameCount++;
|
||||||
} else {
|
} else {
|
||||||
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
|
Warning("Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size());
|
||||||
}
|
}
|
||||||
mFrame.clear();
|
mFrame.clear();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( mFrame.size() ) {
|
if ( mFrame.size() ) {
|
||||||
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
|
Warning("Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size());
|
||||||
} else {
|
} else {
|
||||||
Warning( "Discarding frame %d", mFrameCount );
|
Warning("Discarding frame %d", mFrameCount);
|
||||||
}
|
}
|
||||||
mFrameGood = false;
|
mFrameGood = false;
|
||||||
mFrame.clear();
|
mFrame.clear();
|
||||||
|
@ -335,7 +348,7 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::getFrame( Buffer &buffer ) {
|
bool RtpSource::getFrame(Buffer &buffer) {
|
||||||
if ( !mFrameReady.getValueImmediate() ) {
|
if ( !mFrameReady.getValueImmediate() ) {
|
||||||
Debug(3, "Getting frame but not ready");
|
Debug(3, "Getting frame but not ready");
|
||||||
// Allow for a couple of spurious returns
|
// Allow for a couple of spurious returns
|
||||||
|
|
|
@ -94,7 +94,7 @@ private:
|
||||||
ThreadData<bool> mFrameProcessed;
|
ThreadData<bool> mFrameProcessed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init( uint16_t seq );
|
void init(uint16_t seq);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||||
|
|
148
src/zm_rtsp.cpp
148
src/zm_rtsp.cpp
|
@ -36,15 +36,15 @@ int RtspThread::smMinDataPort = 0;
|
||||||
int RtspThread::smMaxDataPort = 0;
|
int RtspThread::smMaxDataPort = 0;
|
||||||
RtspThread::PortSet RtspThread::smAssignedPorts;
|
RtspThread::PortSet RtspThread::smAssignedPorts;
|
||||||
|
|
||||||
bool RtspThread::sendCommand( std::string message ) {
|
bool RtspThread::sendCommand(std::string message) {
|
||||||
if ( mNeedAuth ) {
|
if ( mNeedAuth ) {
|
||||||
StringVector parts = split( message, " " );
|
StringVector parts = split(message, " ");
|
||||||
if (parts.size() > 1)
|
if ( parts.size() > 1 )
|
||||||
message += mAuthenticator->getAuthHeader(parts[0], parts[1]);
|
message += mAuthenticator->getAuthHeader(parts[0], parts[1]);
|
||||||
}
|
}
|
||||||
message += stringtf( "User-Agent: ZoneMinder/%s\r\n", ZM_VERSION );
|
message += stringtf("User-Agent: ZoneMinder/%s\r\n", ZM_VERSION);
|
||||||
message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq );
|
message += stringtf("CSeq: %d\r\n\r\n", ++mSeq);
|
||||||
Debug( 2, "Sending RTSP message: %s", message.c_str() );
|
Debug(2, "Sending RTSP message: %s", message.c_str());
|
||||||
if ( mMethod == RTP_RTSP_HTTP ) {
|
if ( mMethod == RTP_RTSP_HTTP ) {
|
||||||
message = base64Encode(message);
|
message = base64Encode(message);
|
||||||
Debug(2, "Sending encoded RTSP message: %s", message.c_str());
|
Debug(2, "Sending encoded RTSP message: %s", message.c_str());
|
||||||
|
@ -61,8 +61,8 @@ bool RtspThread::sendCommand( std::string message ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtspThread::recvResponse( std::string &response ) {
|
bool RtspThread::recvResponse(std::string &response) {
|
||||||
if ( mRtspSocket.recv( response ) < 0 )
|
if ( mRtspSocket.recv(response) < 0 )
|
||||||
Error("Recv failed; %s", strerror(errno));
|
Error("Recv failed; %s", strerror(errno));
|
||||||
Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size());
|
Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size());
|
||||||
float respVer = 0;
|
float respVer = 0;
|
||||||
|
@ -74,12 +74,12 @@ bool RtspThread::recvResponse( std::string &response ) {
|
||||||
} else {
|
} else {
|
||||||
Error("Response parse failure, %zd bytes follow", response.size());
|
Error("Response parse failure, %zd bytes follow", response.size());
|
||||||
if ( response.size() )
|
if ( response.size() )
|
||||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
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");
|
Debug(2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
|
||||||
mAuthenticator->checkAuthResponse(response);
|
mAuthenticator->checkAuthResponse(response);
|
||||||
mNeedAuth = true;
|
mNeedAuth = true;
|
||||||
return false;
|
return false;
|
||||||
|
@ -88,24 +88,24 @@ bool RtspThread::recvResponse( std::string &response ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} // end RtspThread::recResponse
|
} // end RtspThread::recvResponse
|
||||||
|
|
||||||
int RtspThread::requestPorts() {
|
int RtspThread::requestPorts() {
|
||||||
if ( !smMinDataPort ) {
|
if ( !smMinDataPort ) {
|
||||||
char sql[ZM_SQL_SML_BUFSIZ];
|
char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
|
//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) );
|
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 ) ) {
|
if ( mysql_query(&dbconn, sql) ) {
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||||
if ( !result ) {
|
if ( !result ) {
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
int nMonitors = mysql_num_rows( result );
|
int nMonitors = mysql_num_rows(result);
|
||||||
int position = 0;
|
int position = 0;
|
||||||
if ( nMonitors ) {
|
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++ ) {
|
||||||
|
@ -124,7 +124,7 @@ int RtspThread::requestPorts() {
|
||||||
int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors);
|
int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors);
|
||||||
smMinDataPort = config.min_rtp_port + (position * portRange);
|
smMinDataPort = config.min_rtp_port + (position * portRange);
|
||||||
smMaxDataPort = smMinDataPort + portRange - 1;
|
smMaxDataPort = smMinDataPort + portRange - 1;
|
||||||
Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort );
|
Debug(2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort);
|
||||||
}
|
}
|
||||||
for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) {
|
for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) {
|
||||||
PortSet::const_iterator iter = smAssignedPorts.find(i);
|
PortSet::const_iterator iter = smAssignedPorts.find(i);
|
||||||
|
@ -137,7 +137,7 @@ int RtspThread::requestPorts() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtspThread::releasePorts( int port ) {
|
void RtspThread::releasePorts(int port) {
|
||||||
if ( port > 0 )
|
if ( port > 0 )
|
||||||
smAssignedPorts.erase(port);
|
smAssignedPorts.erase(port);
|
||||||
}
|
}
|
||||||
|
@ -151,21 +151,21 @@ RtspThread::RtspThread(
|
||||||
const std::string &path,
|
const std::string &path,
|
||||||
const std::string &auth,
|
const std::string &auth,
|
||||||
bool rtsp_describe) :
|
bool rtsp_describe) :
|
||||||
mId( id ),
|
mId(id),
|
||||||
mMethod( method ),
|
mMethod(method),
|
||||||
mProtocol( protocol ),
|
mProtocol(protocol),
|
||||||
mHost( host ),
|
mHost(host),
|
||||||
mPort( port ),
|
mPort(port),
|
||||||
mPath( path ),
|
mPath(path),
|
||||||
mRtspDescribe( rtsp_describe ),
|
mRtspDescribe(rtsp_describe),
|
||||||
mSessDesc( 0 ),
|
mSessDesc(0),
|
||||||
mFormatContext( 0 ),
|
mFormatContext(0),
|
||||||
mSeq( 0 ),
|
mSeq(0),
|
||||||
mSession( 0 ),
|
mSession(0),
|
||||||
mSsrc( 0 ),
|
mSsrc(0),
|
||||||
mDist( UNDEFINED ),
|
mDist(UNDEFINED),
|
||||||
mRtpTime( 0 ),
|
mRtpTime(0),
|
||||||
mStop( false )
|
mStop(false)
|
||||||
{
|
{
|
||||||
mUrl = mProtocol+"://"+mHost+":"+mPort;
|
mUrl = mProtocol+"://"+mHost+":"+mPort;
|
||||||
if ( !mPath.empty() ) {
|
if ( !mPath.empty() ) {
|
||||||
|
@ -177,14 +177,15 @@ RtspThread::RtspThread(
|
||||||
|
|
||||||
mSsrc = rand();
|
mSsrc = rand();
|
||||||
|
|
||||||
Debug(2, "RTSP Local SSRC is %x", mSsrc);
|
Debug(2, "RTSP Local SSRC is %x, url is %s", mSsrc, mUrl.c_str());
|
||||||
|
|
||||||
if ( mMethod == RTP_RTSP_HTTP )
|
if ( mMethod == RTP_RTSP_HTTP )
|
||||||
mHttpSession = stringtf("%d", rand());
|
mHttpSession = stringtf("%d", rand());
|
||||||
|
|
||||||
mNeedAuth = false;
|
mNeedAuth = false;
|
||||||
StringVector parts = split(auth,":");
|
StringVector parts = split(auth, ":");
|
||||||
if (parts.size() > 1)
|
Debug(2, "# of auth parts %d", parts.size());
|
||||||
|
if ( parts.size() > 1 )
|
||||||
mAuthenticator = new zm::Authenticator(parts[0], parts[1]);
|
mAuthenticator = new zm::Authenticator(parts[0], parts[1]);
|
||||||
else
|
else
|
||||||
mAuthenticator = new zm::Authenticator(parts[0], "");
|
mAuthenticator = new zm::Authenticator(parts[0], "");
|
||||||
|
@ -193,9 +194,9 @@ RtspThread::RtspThread(
|
||||||
RtspThread::~RtspThread() {
|
RtspThread::~RtspThread() {
|
||||||
if ( mFormatContext ) {
|
if ( mFormatContext ) {
|
||||||
#if LIBAVFORMAT_VERSION_CHECK(52, 96, 0, 96, 0)
|
#if LIBAVFORMAT_VERSION_CHECK(52, 96, 0, 96, 0)
|
||||||
avformat_free_context( mFormatContext );
|
avformat_free_context(mFormatContext);
|
||||||
#else
|
#else
|
||||||
av_free_format_context( mFormatContext );
|
av_free_format_context(mFormatContext);
|
||||||
#endif
|
#endif
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
}
|
}
|
||||||
|
@ -204,16 +205,17 @@ RtspThread::~RtspThread() {
|
||||||
mSessDesc = NULL;
|
mSessDesc = NULL;
|
||||||
}
|
}
|
||||||
delete mAuthenticator;
|
delete mAuthenticator;
|
||||||
|
mAuthenticator = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtspThread::run() {
|
int RtspThread::run() {
|
||||||
std::string message;
|
std::string message;
|
||||||
std::string response;
|
std::string response;
|
||||||
|
|
||||||
response.reserve( ZM_NETWORK_BUFSIZ );
|
response.reserve(ZM_NETWORK_BUFSIZ);
|
||||||
|
|
||||||
if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) )
|
if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) )
|
||||||
Fatal( "Unable to connect RTSP socket" );
|
Fatal("Unable to connect RTSP socket");
|
||||||
//Select select( 0.25 );
|
//Select select( 0.25 );
|
||||||
//select.addReader( &mRtspSocket );
|
//select.addReader( &mRtspSocket );
|
||||||
//while ( select.wait() )
|
//while ( select.wait() )
|
||||||
|
@ -222,7 +224,6 @@ int RtspThread::run() {
|
||||||
//Debug( 4, "Drained %d bytes from RTSP socket", response.size() );
|
//Debug( 4, "Drained %d bytes from RTSP socket", response.size() );
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
bool authTried = false;
|
bool authTried = false;
|
||||||
if ( mMethod == RTP_RTSP_HTTP ) {
|
if ( mMethod == RTP_RTSP_HTTP ) {
|
||||||
if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) )
|
if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) )
|
||||||
|
@ -245,14 +246,13 @@ int RtspThread::run() {
|
||||||
message += mAuthenticator->getAuthHeader("GET", mPath);
|
message += mAuthenticator->getAuthHeader("GET", mPath);
|
||||||
authTried = true;
|
authTried = true;
|
||||||
}
|
}
|
||||||
message += "Accept: application/x-rtsp-tunnelled\r\n";
|
message += "Accept: application/x-rtsp-tunnelled\r\n\r\n";
|
||||||
message += "\r\n";
|
Debug(2, "Sending HTTP message: %s", message.c_str());
|
||||||
Debug( 2, "Sending HTTP message: %s", message.c_str() );
|
if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) {
|
||||||
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
|
|
||||||
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if ( mRtspSocket.recv( response ) < 0 ) {
|
if ( mRtspSocket.recv(response) < 0 ) {
|
||||||
Error("Recv failed; %s", strerror(errno));
|
Error("Recv failed; %s", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -260,13 +260,13 @@ int RtspThread::run() {
|
||||||
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;
|
float respVer = 0;
|
||||||
respCode = -1;
|
respCode = -1;
|
||||||
if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) {
|
if ( sscanf(response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) {
|
||||||
if ( isalnum(response[0]) ) {
|
if ( isalnum(response[0]) ) {
|
||||||
Error( "Response parse failure in '%s'", response.c_str() );
|
Error("Response parse failure in '%s'", response.c_str());
|
||||||
} else {
|
} else {
|
||||||
Error( "Response parse failure, %zd bytes follow", response.size() );
|
Error("Response parse failure, %zd bytes follow", response.size());
|
||||||
if ( response.size() )
|
if ( response.size() )
|
||||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
Hexdump(Logger::ERROR, response.data(), min(response.size(),16));
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -296,25 +296,25 @@ int RtspThread::run() {
|
||||||
message += "Content-Length: 32767\r\n";
|
message += "Content-Length: 32767\r\n";
|
||||||
message += "Content-Type: application/x-rtsp-tunnelled\r\n";
|
message += "Content-Type: application/x-rtsp-tunnelled\r\n";
|
||||||
message += "\r\n";
|
message += "\r\n";
|
||||||
Debug( 2, "Sending HTTP message: %s", message.c_str() );
|
Debug(2, "Sending HTTP message: %s", message.c_str());
|
||||||
if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) {
|
if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) {
|
||||||
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) );
|
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} // end if ( mMethod == RTP_RTSP_HTTP )
|
||||||
|
|
||||||
std::string localHost = "";
|
std::string localHost = "";
|
||||||
int localPorts[2] = { 0, 0 };
|
int localPorts[2] = { 0, 0 };
|
||||||
|
|
||||||
// Request supported RTSP commands by the server
|
// Request supported RTSP commands by the server
|
||||||
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
|
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
|
||||||
if ( !sendCommand( message ) )
|
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.
|
// 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 ) {
|
if ( mNeedAuth ) {
|
||||||
Debug( 2, "Resending OPTIONS due to possible auth requirement" );
|
Debug(2, "Resending OPTIONS due to possible auth requirement");
|
||||||
if ( !sendCommand(message) )
|
if ( !sendCommand(message) )
|
||||||
return -1;
|
return -1;
|
||||||
if ( !recvResponse(response) )
|
if ( !recvResponse(response) )
|
||||||
|
@ -338,11 +338,11 @@ int RtspThread::run() {
|
||||||
message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n";
|
message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n";
|
||||||
bool res;
|
bool res;
|
||||||
do {
|
do {
|
||||||
if (mNeedAuth)
|
if ( mNeedAuth )
|
||||||
authTried = true;
|
authTried = true;
|
||||||
sendCommand(message);
|
sendCommand(message);
|
||||||
// FIXME WHy sleep 1?
|
// FIXME Why sleep 1?
|
||||||
sleep(1);
|
usleep(10000);
|
||||||
res = recvResponse(response);
|
res = recvResponse(response);
|
||||||
if ( !res && respCode==401 )
|
if ( !res && respCode==401 )
|
||||||
mNeedAuth = true;
|
mNeedAuth = true;
|
||||||
|
@ -600,7 +600,8 @@ int RtspThread::run() {
|
||||||
while( !mStop ) {
|
while( !mStop ) {
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||||
Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
|
Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d",
|
||||||
|
sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) );
|
||||||
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) {
|
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) {
|
||||||
if ( !sendCommand( message ) )
|
if ( !sendCommand( message ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -678,22 +679,21 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( channel == remoteChannels[0] ) {
|
if ( channel == remoteChannels[0] ) {
|
||||||
Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len );
|
Debug(4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len);
|
||||||
Hexdump( 4, (char *)buffer, 16 );
|
Hexdump(4, (char *)buffer, 16);
|
||||||
rtpDataThread.recvPacket( buffer+4, len );
|
rtpDataThread.recvPacket(buffer+4, len);
|
||||||
Debug( 4, "Received" );
|
|
||||||
} else if ( channel == remoteChannels[1] ) {
|
} else if ( channel == remoteChannels[1] ) {
|
||||||
// len = ntohs( *((unsigned short *)(buffer+2)) );
|
// 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", nBytes, channel );
|
||||||
Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
|
Debug(4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len);
|
||||||
Hexdump( 4, (char *)buffer, 16 );
|
Hexdump(4, (char *)buffer, 16);
|
||||||
rtpCtrlThread.recvPackets( buffer+4, len );
|
rtpCtrlThread.recvPackets(buffer+4, len);
|
||||||
} else {
|
} else {
|
||||||
Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
|
Error("Unexpected channel selector %d in RTSP interleaved data", buffer[1]);
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buffer.consume( len+4 );
|
buffer.consume(len+4);
|
||||||
nBytes -= len+4;
|
nBytes -= len+4;
|
||||||
} else {
|
} else {
|
||||||
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
|
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
|
||||||
|
|
|
@ -56,21 +56,20 @@ void Authenticator::reset() {
|
||||||
fAuthMethod = AUTH_UNDEFINED;
|
fAuthMethod = AUTH_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::authHandleHeader(std::string headerData)
|
void Authenticator::authHandleHeader(std::string headerData) {
|
||||||
{
|
|
||||||
const char* basic_match = "Basic ";
|
const char* basic_match = "Basic ";
|
||||||
const char* digest_match = "Digest ";
|
const char* digest_match = "Digest ";
|
||||||
size_t digest_match_len = strlen(digest_match);
|
size_t digest_match_len = strlen(digest_match);
|
||||||
|
|
||||||
// Check if basic auth
|
// Check if basic auth
|
||||||
if ( strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0 ) {
|
if ( strncasecmp(headerData.c_str(), basic_match, strlen(basic_match)) == 0 ) {
|
||||||
fAuthMethod = AUTH_BASIC;
|
fAuthMethod = AUTH_BASIC;
|
||||||
Debug(2, "Set authMethod to Basic");
|
Debug(2, "Set authMethod to Basic");
|
||||||
}
|
}
|
||||||
// Check if digest auth
|
// Check if digest auth
|
||||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0) {
|
else if ( strncasecmp(headerData.c_str(), digest_match, digest_match_len) == 0) {
|
||||||
fAuthMethod = AUTH_DIGEST;
|
fAuthMethod = AUTH_DIGEST;
|
||||||
Debug( 2, "Set authMethod to Digest");
|
Debug(2, "Set authMethod to Digest");
|
||||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||||
// subparts are key="value"
|
// subparts are key="value"
|
||||||
for ( size_t i = 0; i < subparts.size(); i++ ) {
|
for ( size_t i = 0; i < subparts.size(); i++ ) {
|
||||||
|
@ -92,7 +91,7 @@ void Authenticator::authHandleHeader(std::string headerData)
|
||||||
Debug(2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s",
|
Debug(2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s",
|
||||||
username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str());
|
username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str());
|
||||||
}
|
}
|
||||||
}
|
} // end void Authenticator::authHandleHeader(std::string headerData)
|
||||||
|
|
||||||
std::string Authenticator::quote( const std::string &src ) {
|
std::string Authenticator::quote( const std::string &src ) {
|
||||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||||
|
@ -100,13 +99,13 @@ std::string Authenticator::quote( const std::string &src ) {
|
||||||
|
|
||||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri) {
|
std::string Authenticator::getAuthHeader(std::string method, std::string uri) {
|
||||||
std::string result = "Authorization: ";
|
std::string result = "Authorization: ";
|
||||||
if (fAuthMethod == AUTH_BASIC) {
|
if ( fAuthMethod == AUTH_BASIC ) {
|
||||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
result += "Basic " + base64Encode(username() + ":" + password());
|
||||||
} else if (fAuthMethod == AUTH_DIGEST) {
|
} else if ( fAuthMethod == AUTH_DIGEST ) {
|
||||||
result += std::string("Digest ") +
|
result += std::string("Digest ") +
|
||||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||||
if ( ! fQop.empty() ) {
|
if ( !fQop.empty() ) {
|
||||||
result += ", qop=" + fQop;
|
result += ", qop=" + fQop;
|
||||||
result += ", nc=" + stringtf("%08x",nc);
|
result += ", nc=" + stringtf("%08x",nc);
|
||||||
result += ", cnonce=\"" + fCnonce + "\"";
|
result += ", cnonce=\"" + fCnonce + "\"";
|
||||||
|
@ -203,21 +202,21 @@ void Authenticator::checkAuthResponse(std::string &response) {
|
||||||
|
|
||||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||||
// stop at end of headers
|
// stop at end of headers
|
||||||
if (lines[i].length()==0)
|
if ( lines[i].length() == 0 )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
if ( strncasecmp(lines[i].c_str(), authenticate_match, authenticate_match_len) == 0 ) {
|
||||||
authLine = lines[i];
|
authLine = lines[i];
|
||||||
Debug( 2, "Found auth line at %d", i);
|
Debug(2, "Found auth line at %d:", i);
|
||||||
break;
|
//break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!authLine.empty()) {
|
if ( !authLine.empty() ) {
|
||||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
Debug(2, "Analyze auth line %s", authLine.c_str());
|
||||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
authHandleHeader(trimSpaces(authLine.substr(authenticate_match_len, authLine.length()-authenticate_match_len)));
|
||||||
} else {
|
} else {
|
||||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
Debug(2, "Didn't find auth line in %s", authLine.c_str());
|
||||||
}
|
}
|
||||||
}
|
} // end void Authenticator::checkAuthResponse(std::string &response)
|
||||||
|
|
||||||
} // namespace zm
|
} // namespace zm
|
||||||
|
|
|
@ -376,6 +376,9 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const {
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
Warning("Unknown media_type %s", type.c_str());
|
Warning("Unknown media_type %s", type.c_str());
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
stream->codecpar->codec_type = codec_context->codec_type;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
std::string codec_name;
|
std::string codec_name;
|
||||||
|
|
|
@ -116,25 +116,32 @@ Condition::~Condition() {
|
||||||
|
|
||||||
void Condition::wait() {
|
void Condition::wait() {
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
if ( pthread_cond_wait(&mCondition, mMutex.getMutex()) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException(stringtf("Unable to wait pthread condition: %s", strerror(errno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( int secs ) {
|
bool Condition::wait(int secs) {
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
Debug( 8, "Waiting for %d seconds", secs );
|
Debug(8, "Waiting for %d seconds", secs);
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout(secs);
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if (
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
( pthread_cond_timedwait(&mCondition, mMutex.getMutex(), &timeout) < 0 )
|
||||||
return( errno != ETIMEDOUT );
|
&&
|
||||||
|
( errno != ETIMEDOUT )
|
||||||
|
)
|
||||||
|
throw ThreadException(stringtf("Unable to timedwait pthread condition: %s", strerror(errno)));
|
||||||
|
return errno != ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( double secs ) {
|
bool Condition::wait( double secs ) {
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if (
|
||||||
|
(pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0)
|
||||||
|
&&
|
||||||
|
(errno != ETIMEDOUT) )
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||||
return( errno != ETIMEDOUT );
|
return errno != ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::signal() {
|
void Condition::signal() {
|
||||||
|
@ -177,11 +184,11 @@ template <class T> const T ThreadData<T>::getUpdatedValue(double secs) const {
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait( secs );
|
mCondition.wait(secs);
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug(9, "Got value update, %p", this );
|
Debug(9, "Got value update, %p", this);
|
||||||
return valueCopy;
|
return valueCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,9 @@ private:
|
||||||
pthread_t pid() { return( pthread_self() ); }
|
pthread_t pid() { return( pthread_self() ); }
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
explicit ThreadException( const std::string &message ) : Exception( stringtf("(%d) ", (long int)pid())+message ) {
|
explicit ThreadException(const std::string &message) :
|
||||||
|
Exception(stringtf("(%d) ", (long int)pid())+message)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,38 +171,36 @@ private:
|
||||||
mutable Condition mCondition;
|
mutable Condition mCondition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
__attribute__((used)) ThreadData() : mValue(0), mCondition( mMutex ) {
|
__attribute__((used)) ThreadData() :
|
||||||
|
mValue(0), mCondition(mMutex)
|
||||||
|
{
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
}
|
}
|
||||||
explicit __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) {
|
explicit __attribute__((used)) ThreadData(T value) :
|
||||||
|
mValue(value), mCondition(mMutex) {
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
}
|
}
|
||||||
//~ThreadData() {}
|
|
||||||
|
|
||||||
__attribute__((used)) operator T() const
|
__attribute__((used)) operator T() const {
|
||||||
{
|
return getValue();
|
||||||
return( getValue() );
|
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T operator=( const T value )
|
__attribute__((used)) const T operator=( const T value ) {
|
||||||
{
|
return setValue(value);
|
||||||
return( setValue( value ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((used)) const T getValueImmediate() const
|
__attribute__((used)) const T getValueImmediate() const {
|
||||||
{
|
return mValue;
|
||||||
return( mValue );
|
|
||||||
}
|
}
|
||||||
__attribute__((used)) T setValueImmediate( const T value )
|
__attribute__((used)) T setValueImmediate( const T value ) {
|
||||||
{
|
return mValue = value;
|
||||||
return( mValue = value );
|
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T getValue() const;
|
__attribute__((used)) const T getValue() const;
|
||||||
__attribute__((used)) T setValue( const T value );
|
__attribute__((used)) T setValue( const T value );
|
||||||
__attribute__((used)) const T getUpdatedValue() const;
|
__attribute__((used)) const T getUpdatedValue() const;
|
||||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
__attribute__((used)) const T getUpdatedValue(double secs) const;
|
||||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
__attribute__((used)) const T getUpdatedValue(int secs) const;
|
||||||
__attribute__((used)) void updateValueSignal( const T value );
|
__attribute__((used)) void updateValueSignal(const T value);
|
||||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
__attribute__((used)) void updateValueBroadcast(const T value);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
|
@ -19,7 +19,7 @@ $statusData = array(
|
||||||
'limit' => 1,
|
'limit' => 1,
|
||||||
'elements' => array(
|
'elements' => array(
|
||||||
'MonitorCount' => array( 'sql' => 'count(*)' ),
|
'MonitorCount' => array( 'sql' => 'count(*)' ),
|
||||||
'ActiveMonitorCount' => array( 'sql' => 'count(if(Function != \'None\',1,NULL))' ),
|
'ActiveMonitorCount' => array( 'sql' => 'count(if(`Function` != \'None\',1,NULL))' ),
|
||||||
'State' => array( 'func' => 'daemonCheck()?'.translate('Running').':'.translate('Stopped') ),
|
'State' => array( 'func' => 'daemonCheck()?'.translate('Running').':'.translate('Stopped') ),
|
||||||
'Load' => array( 'func' => 'getLoad()' ),
|
'Load' => array( 'func' => 'getLoad()' ),
|
||||||
'Disk' => array( 'func' => 'getDiskPercent()' ),
|
'Disk' => array( 'func' => 'getDiskPercent()' ),
|
||||||
|
@ -211,17 +211,17 @@ function collectData() {
|
||||||
$values = array();
|
$values = array();
|
||||||
|
|
||||||
$elements = &$entitySpec['elements'];
|
$elements = &$entitySpec['elements'];
|
||||||
$lc_elements = array_change_key_case( $elements );
|
$lc_elements = array_change_key_case($elements);
|
||||||
|
|
||||||
$id = false;
|
$id = false;
|
||||||
if ( isset($_REQUEST['id']) )
|
if ( isset($_REQUEST['id']) )
|
||||||
if ( !is_array($_REQUEST['id']) )
|
if ( !is_array($_REQUEST['id']) )
|
||||||
$id = array( validJsStr($_REQUEST['id']) );
|
$id = array( validJsStr($_REQUEST['id']) );
|
||||||
else
|
else
|
||||||
$id = array_values( $_REQUEST['id'] );
|
$id = array_values($_REQUEST['id']);
|
||||||
|
|
||||||
if ( !isset($_REQUEST['element']) )
|
if ( !isset($_REQUEST['element']) )
|
||||||
$_REQUEST['element'] = array_keys( $elements );
|
$_REQUEST['element'] = array_keys($elements);
|
||||||
else if ( !is_array($_REQUEST['element']) )
|
else if ( !is_array($_REQUEST['element']) )
|
||||||
$_REQUEST['element'] = array( validJsStr($_REQUEST['element']) );
|
$_REQUEST['element'] = array( validJsStr($_REQUEST['element']) );
|
||||||
|
|
||||||
|
@ -235,18 +235,18 @@ function collectData() {
|
||||||
|
|
||||||
foreach ( $_REQUEST['element'] as $element ) {
|
foreach ( $_REQUEST['element'] as $element ) {
|
||||||
if ( !($elementData = $lc_elements[strtolower($element)]) )
|
if ( !($elementData = $lc_elements[strtolower($element)]) )
|
||||||
ajaxError( 'Bad '.validJsStr($_REQUEST['entity']).' element '.$element );
|
ajaxError('Bad '.validJsStr($_REQUEST['entity']).' element '.$element);
|
||||||
if ( isset($elementData['func']) )
|
if ( isset($elementData['func']) )
|
||||||
$data[$element] = eval( 'return( '.$elementData['func'].' );' );
|
$data[$element] = eval('return( '.$elementData['func'].' );');
|
||||||
else if ( isset($elementData['postFunc']) )
|
else if ( isset($elementData['postFunc']) )
|
||||||
$postFuncs[$element] = $elementData['postFunc'];
|
$postFuncs[$element] = $elementData['postFunc'];
|
||||||
else if ( isset($elementData['zmu']) )
|
else if ( isset($elementData['zmu']) )
|
||||||
$data[$element] = exec( escapeshellcmd( getZmuCommand( ' '.$elementData['zmu'] ) ) );
|
$data[$element] = exec(escapeshellcmd(getZmuCommand(' '.$elementData['zmu'])));
|
||||||
else {
|
else {
|
||||||
if ( isset($elementData['sql']) )
|
if ( isset($elementData['sql']) )
|
||||||
$fieldSql[] = $elementData['sql'].' as '.$element;
|
$fieldSql[] = $elementData['sql'].' as '.$element;
|
||||||
else
|
else
|
||||||
$fieldSql[] = $element;
|
$fieldSql[] = '`'.$element.'`';
|
||||||
if ( isset($elementData['table']) && isset($elementData['join']) ) {
|
if ( isset($elementData['table']) && isset($elementData['join']) ) {
|
||||||
$joinSql[] = 'left join '.$elementData['table'].' on '.$elementData['join'];
|
$joinSql[] = 'left join '.$elementData['table'].' on '.$elementData['join'];
|
||||||
}
|
}
|
||||||
|
@ -296,7 +296,7 @@ function collectData() {
|
||||||
$sql .= ' '.strtoupper($matches[3]);
|
$sql .= ' '.strtoupper($matches[3]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ZM\Error("Sort field didn't match regexp $sort_field");
|
ZM\Error('Sort field didn\'t match regexp '.$sort_field);
|
||||||
}
|
}
|
||||||
} # end foreach sort field
|
} # end foreach sort field
|
||||||
} # end if has sort
|
} # end if has sort
|
||||||
|
@ -310,7 +310,7 @@ function collectData() {
|
||||||
if ( !empty( $limit ) )
|
if ( !empty( $limit ) )
|
||||||
$sql .= ' limit '.$limit_offset.$limit;
|
$sql .= ' limit '.$limit_offset.$limit;
|
||||||
if ( isset($limit) && $limit == 1 ) {
|
if ( isset($limit) && $limit == 1 ) {
|
||||||
if ( $sqlData = dbFetchOne( $sql, NULL, $values ) ) {
|
if ( $sqlData = dbFetchOne($sql, NULL, $values) ) {
|
||||||
foreach ( $postFuncs as $element=>$func )
|
foreach ( $postFuncs as $element=>$func )
|
||||||
$sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' );
|
$sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' );
|
||||||
$data = array_merge( $data, $sqlData );
|
$data = array_merge( $data, $sqlData );
|
||||||
|
|
|
@ -32,7 +32,7 @@ if ( !empty($_REQUEST['group']) ) {
|
||||||
|
|
||||||
$mid = !empty($_REQUEST['mid']) ? validInt($_REQUEST['mid']) : 0;
|
$mid = !empty($_REQUEST['mid']) ? validInt($_REQUEST['mid']) : 0;
|
||||||
|
|
||||||
$sql = "SELECT m.* FROM Monitors m WHERE m.Function != 'None' AND m.Controllable = 1$groupSql ORDER BY Sequence";
|
$sql = "SELECT m.* FROM Monitors m WHERE m.`Function` != 'None' AND m.`Controllable` = 1$groupSql ORDER BY `Sequence`";
|
||||||
$mids = array();
|
$mids = array();
|
||||||
foreach ( dbFetchAll($sql, false, $params) as $row ) {
|
foreach ( dbFetchAll($sql, false, $params) as $row ) {
|
||||||
if ( !visibleMonitor($row['Id']) ) {
|
if ( !visibleMonitor($row['Id']) ) {
|
||||||
|
|
|
@ -71,7 +71,7 @@ if ( canView('Control') && $monitor->Type() == 'Local' ) {
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div id="scaleControl"><?php echo translate('Scale') ?>: <?php echo htmlSelect('scale', $scales, $scale); ?></div>
|
<div id="scaleControl"><?php echo translate('Scale').': '.htmlSelect('scale', $scales, $scale); ?></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="closeControl"><a href="#" data-on-click="<?php echo $popup ? 'closeWindow' : 'backWindow' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
|
<div id="closeControl"><a href="#" data-on-click="<?php echo $popup ? 'closeWindow' : 'backWindow' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue