merge all the commits from the messed up iconnor_video branch
This commit is contained in:
parent
f73d8be765
commit
9a795432cf
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
cmake_minimum_required (VERSION 2.6)
|
cmake_minimum_required (VERSION 2.6)
|
||||||
project (zoneminder)
|
project (zoneminder)
|
||||||
set(zoneminder_VERSION "1.29.0")
|
set(zoneminder_VERSION "1.30.0")
|
||||||
# make API version a minor of ZM version
|
# make API version a minor of ZM version
|
||||||
set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")
|
set(zoneminder_API_VERSION "${zoneminder_VERSION}.1")
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ ZoneMinder H264 Patch
|
||||||
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png?branch=feature-h264-videostorage)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png?branch=feature-h264-videostorage)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||||
|
|
||||||
##Feature-h264-videostorage Branch Details
|
##Feature-h264-videostorage Branch Details
|
||||||
This branch supports direct recording of h264 cameras into MP4 format uisng the h264 Passthrough option, but only with FFMPEG Monitors currently. It also it provides h264 encoding from jpegs of any other monitor type and local or mpeg cameras. If you encounter any issues, please open an issue on GitHub and attach it to the h264 milestone. But do remember this is bleeding edge so it will have problems.
|
This branch supports direct recording of h264 cameras into MP4 format uisng the h264 Passthrough option. As well it provides h264 encoding for local or mpeg cameras. If you encounter any issues, please open an issue on GitHub and attach it to the h264 milestone. But do remember this is bleeding edge so it will have problems.
|
||||||
Thanks to @chriswiggins and @mastertheknife for their work, @SteveGilvarry is now maintaining this branch and welcomes any assistance.
|
Thanks to @chriswiggins and @mastertheknife for their work, @SteveGilvarry is now maintaining this branch and welcomes any assistance.
|
||||||
|
|
||||||
**The following SQL changes are required, these will be merged to zmupdate once we are ready to merge this branch to master.**
|
**The following SQL changes are required, these will be merged to zmupdate once we are ready to merge this branch to master.**
|
||||||
|
|
|
@ -193,6 +193,7 @@ CREATE TABLE `Events` (
|
||||||
`Length` decimal(10,2) NOT NULL default '0.00',
|
`Length` decimal(10,2) NOT NULL default '0.00',
|
||||||
`Frames` int(10) unsigned default NULL,
|
`Frames` int(10) unsigned default NULL,
|
||||||
`AlarmFrames` int(10) unsigned default NULL,
|
`AlarmFrames` int(10) unsigned default NULL,
|
||||||
|
`DefaultVideo` VARCHAR( 64 ) NOT NULL,
|
||||||
`TotScore` int(10) unsigned NOT NULL default '0',
|
`TotScore` int(10) unsigned NOT NULL default '0',
|
||||||
`AvgScore` smallint(5) unsigned default '0',
|
`AvgScore` smallint(5) unsigned default '0',
|
||||||
`MaxScore` smallint(5) unsigned default '0',
|
`MaxScore` smallint(5) unsigned default '0',
|
||||||
|
@ -344,6 +345,10 @@ CREATE TABLE `Monitors` (
|
||||||
`Palette` int(10) unsigned NOT NULL default '0',
|
`Palette` int(10) unsigned NOT NULL default '0',
|
||||||
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
|
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
|
||||||
`Deinterlacing` int(10) unsigned NOT NULL default '0',
|
`Deinterlacing` int(10) unsigned NOT NULL default '0',
|
||||||
|
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
|
||||||
|
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
|
||||||
|
`EncoderParameters` TEXT NOT NULL,
|
||||||
|
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
|
||||||
`RTSPDescribe` tinyint(1) unsigned NOT NULL default '0',
|
`RTSPDescribe` tinyint(1) unsigned NOT NULL default '0',
|
||||||
`Brightness` mediumint(7) NOT NULL default '-1',
|
`Brightness` mediumint(7) NOT NULL default '-1',
|
||||||
`Contrast` mediumint(7) NOT NULL default '-1',
|
`Contrast` mediumint(7) NOT NULL default '-1',
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
zoneminder (1.29.0+h264-wily-SNAPSHOT2016033101) wily; urgency=medium
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
-- Isaac Connor <iconnor@connortechnology.com> Thu, 31 Mar 2016 10:29:41 -0400
|
||||||
|
|
||||||
zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium
|
zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium
|
||||||
|
|
||||||
* include api, switch to cmake build
|
* include api, switch to cmake build
|
||||||
|
|
|
@ -21,6 +21,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
|
||||||
,libphp-serialization-perl
|
,libphp-serialization-perl
|
||||||
,libsys-mmap-perl [!hurd-any]
|
,libsys-mmap-perl [!hurd-any]
|
||||||
,libwww-perl
|
,libwww-perl
|
||||||
|
,libx264-dev, libmp4v2-dev
|
||||||
# Unbundled (dh_linktree):
|
# Unbundled (dh_linktree):
|
||||||
,libjs-jquery
|
,libjs-jquery
|
||||||
,libjs-mootools
|
,libjs-mootools
|
||||||
|
@ -61,6 +62,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,policykit-1
|
,policykit-1
|
||||||
,rsyslog | system-log-daemon
|
,rsyslog | system-log-daemon
|
||||||
,zip
|
,zip
|
||||||
|
,libmp4v2-2
|
||||||
Recommends: ${misc:Recommends}
|
Recommends: ${misc:Recommends}
|
||||||
,libapache2-mod-php5 | php5-fpm
|
,libapache2-mod-php5 | php5-fpm
|
||||||
,mysql-server | virtual-mysql-server
|
,mysql-server | virtual-mysql-server
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
|
|
||||||
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
id( p_id ),
|
id( p_id ),
|
||||||
type( p_type ),
|
type( p_type ),
|
||||||
width( p_width),
|
width( p_width),
|
||||||
|
@ -31,7 +31,8 @@ Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_co
|
||||||
hue( p_hue ),
|
hue( p_hue ),
|
||||||
colour( p_colour ),
|
colour( p_colour ),
|
||||||
contrast( p_contrast ),
|
contrast( p_contrast ),
|
||||||
capture( p_capture )
|
capture( p_capture ),
|
||||||
|
record_audio( p_record_audio )
|
||||||
{
|
{
|
||||||
pixels = width * height;
|
pixels = width * height;
|
||||||
imagesize = pixels * colours;
|
imagesize = pixels * colours;
|
||||||
|
|
|
@ -47,9 +47,10 @@ protected:
|
||||||
int colour;
|
int colour;
|
||||||
int contrast;
|
int contrast;
|
||||||
bool capture;
|
bool capture;
|
||||||
|
bool record_audio;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
virtual ~Camera();
|
virtual ~Camera();
|
||||||
|
|
||||||
int getId() const { return( id ); }
|
int getId() const { return( id ); }
|
||||||
|
@ -74,13 +75,13 @@ public:
|
||||||
|
|
||||||
bool CanCapture() const { return( capture ); }
|
bool CanCapture() const { return( capture ); }
|
||||||
|
|
||||||
bool SupportsNativeVideo() const { return( type == FFMPEG_SRC ); }
|
bool SupportsNativeVideo() const { return( (type == FFMPEG_SRC )||(type == REMOTE_SRC)); }
|
||||||
|
|
||||||
virtual int PrimeCapture() { return( 0 ); }
|
virtual int PrimeCapture() { return( 0 ); }
|
||||||
virtual int PreCapture()=0;
|
virtual int PreCapture()=0;
|
||||||
virtual int Capture( Image &image )=0;
|
virtual int Capture( Image &image )=0;
|
||||||
virtual int PostCapture()=0;
|
virtual int PostCapture()=0;
|
||||||
virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory)=0;
|
virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory)=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_CAMERA_H
|
#endif // ZM_CAMERA_H
|
||||||
|
|
|
@ -30,8 +30,8 @@ const char* content_type_match = "Content-Type:";
|
||||||
size_t content_length_match_len;
|
size_t content_length_match_len;
|
||||||
size_t content_type_match_len;
|
size_t content_type_match_len;
|
||||||
|
|
||||||
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -313,6 +313,7 @@ int cURLCamera::PostCapture()
|
||||||
|
|
||||||
int cURLCamera::CaptureAndRecord( Image &image, bool recording, char* event_directory )
|
int cURLCamera::CaptureAndRecord( Image &image, bool recording, char* event_directory )
|
||||||
{
|
{
|
||||||
|
Error("Capture and Record not implemented for the cURL camera type");
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ protected:
|
||||||
pthread_cond_t request_complete_cond;
|
pthread_cond_t request_complete_cond;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~cURLCamera();
|
~cURLCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
|
|
|
@ -36,8 +36,8 @@ extern "C"{
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
mMethod( p_method ),
|
mMethod( p_method ),
|
||||||
mOptions( p_options )
|
mOptions( p_options )
|
||||||
|
@ -168,7 +168,7 @@ int FfmpegCamera::Capture( Image &image )
|
||||||
(avResult == -110)
|
(avResult == -110)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf );
|
||||||
ReopenFfmpeg();
|
ReopenFfmpeg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +176,7 @@ int FfmpegCamera::Capture( Image &image )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
||||||
|
// What about audio stream? Maybe someday we could do sound detection...
|
||||||
if ( packet.stream_index == mVideoStreamId )
|
if ( packet.stream_index == mVideoStreamId )
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
|
@ -187,37 +188,38 @@ int FfmpegCamera::Capture( Image &image )
|
||||||
|
|
||||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||||
|
|
||||||
if ( frameComplete )
|
if ( frameComplete ) {
|
||||||
{
|
|
||||||
Debug( 3, "Got frame %d", frameCount );
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if(mConvertContext == NULL) {
|
if(mConvertContext == NULL) {
|
||||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
|
|
||||||
if(mConvertContext == NULL)
|
if(mConvertContext == NULL)
|
||||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
frameCount++;
|
frameCount++;
|
||||||
}
|
} // end if frameComplete
|
||||||
}
|
} else {
|
||||||
|
Debug( 4, "Different stream_index %d", packet.stream_index );
|
||||||
|
} // end if packet.stream_index == mVideoStreamId
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||||
av_packet_unref( &packet);
|
av_packet_unref( &packet);
|
||||||
#else
|
#else
|
||||||
av_free_packet( &packet );
|
av_free_packet( &packet );
|
||||||
#endif
|
#endif
|
||||||
}
|
} // end while ! frameComplete
|
||||||
return (0);
|
return (0);
|
||||||
}
|
} // FfmpegCamera::Capture
|
||||||
|
|
||||||
int FfmpegCamera::PostCapture()
|
int FfmpegCamera::PostCapture()
|
||||||
{
|
{
|
||||||
|
@ -312,6 +314,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
// Find first video stream present
|
// Find first video stream present
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
|
mAudioStreamId = -1;
|
||||||
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
|
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
|
||||||
{
|
{
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
|
@ -320,23 +323,32 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
mVideoStreamId = i;
|
if ( mVideoStreamId == -1 ) {
|
||||||
break;
|
mVideoStreamId = i;
|
||||||
|
// if we break, then we won't find the audio stream
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Debug(2, "Have another video stream." );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(mAudioStreamId == -1) //FIXME best way to copy all other streams?
|
|
||||||
{
|
|
||||||
#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))
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
|
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
|
||||||
#else
|
#else
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
|
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
if ( mAudioStreamId == -1 ) {
|
||||||
mAudioStreamId = i;
|
mAudioStreamId = i;
|
||||||
}
|
} else {
|
||||||
|
Debug(2, "Have another audio stream." );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( mVideoStreamId == -1 )
|
if ( mVideoStreamId == -1 )
|
||||||
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
|
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
|
||||||
|
if ( mAudioStreamId == -1 )
|
||||||
|
Debug( 3, "Unable to locate audio stream in %s", mPath.c_str() );
|
||||||
|
|
||||||
Debug ( 1, "Found video stream" );
|
Debug ( 1, "Found video stream" );
|
||||||
|
|
||||||
|
@ -521,17 +533,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
|
|
||||||
/* Request a writeable buffer of the target image */
|
/* Request a writeable buffer of the target image */
|
||||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
if(directbuffer == NULL) {
|
if( directbuffer == NULL ) {
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
while ( !frameComplete )
|
while ( !frameComplete ) {
|
||||||
{
|
|
||||||
int avResult = av_read_frame( mFormatContext, &packet );
|
int avResult = av_read_frame( mFormatContext, &packet );
|
||||||
if ( avResult < 0 )
|
if ( avResult < 0 ) {
|
||||||
{
|
|
||||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||||
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
if (
|
if (
|
||||||
|
@ -539,8 +549,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
||||||
// Check for Connection failure.
|
// Check for Connection failure.
|
||||||
(avResult == -110)
|
(avResult == -110)
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
||||||
ReopenFfmpeg();
|
ReopenFfmpeg();
|
||||||
}
|
}
|
||||||
|
@ -549,8 +558,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
||||||
if ( packet.stream_index == mVideoStreamId )
|
if ( packet.stream_index == mVideoStreamId ) {
|
||||||
{
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
||||||
#else
|
#else
|
||||||
|
@ -560,8 +568,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
|
|
||||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||||
|
|
||||||
if ( frameComplete )
|
if ( frameComplete ) {
|
||||||
{
|
|
||||||
Debug( 3, "Got frame %d", frameCount );
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||||
|
@ -572,21 +579,23 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
//TODO I think we need to store the key frame location for seeking as part of the event
|
//TODO I think we need to store the key frame location for seeking as part of the event
|
||||||
|
|
||||||
//Video recording
|
//Video recording
|
||||||
if(recording && !wasRecording){
|
if ( recording && !wasRecording ) {
|
||||||
//Instantiate the video storage module
|
//Instantiate the video storage module
|
||||||
|
|
||||||
videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime);
|
videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime);
|
||||||
wasRecording = true;
|
wasRecording = true;
|
||||||
strcpy(oldDirectory, event_file);
|
strcpy(oldDirectory, event_file);
|
||||||
|
|
||||||
}else if(!recording && wasRecording && videoStore){
|
} else if ( ( ! recording ) && wasRecording && videoStore ) {
|
||||||
Info("Deleting videoStore instance");
|
Info("Deleting videoStore instance");
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The directory we are recording to is no longer tied to the current event. Need to re-init the videostore with the correct directory and start recording again
|
//The directory we are recording to is no longer tied to the current event. Need to re-init the videostore with the correct directory and start recording again
|
||||||
if(recording && wasRecording && (strcmp(oldDirectory, event_file)!=0) && (packet.flags & AV_PKT_FLAG_KEY) ){ //don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?...if we store our key frame location with the event will that be enough?
|
if ( recording && wasRecording && (strcmp(oldDirectory, event_file) != 0 ) && (packet.flags & AV_PKT_FLAG_KEY) ) {
|
||||||
|
// don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?...
|
||||||
|
// if we store our key frame location with the event will that be enough?
|
||||||
Info("Re-starting video storage module");
|
Info("Re-starting video storage module");
|
||||||
if(videoStore){
|
if(videoStore){
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
|
@ -597,7 +606,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
strcpy(oldDirectory, event_file);
|
strcpy(oldDirectory, event_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(videoStore && recording){
|
if ( videoStore && recording ) {
|
||||||
//Write the packet to our video store
|
//Write the packet to our video store
|
||||||
int ret = videoStore->writeVideoFramePacket(&packet, mFormatContext->streams[mVideoStreamId]);//, &lastKeyframePkt);
|
int ret = videoStore->writeVideoFramePacket(&packet, mFormatContext->streams[mVideoStreamId]);//, &lastKeyframePkt);
|
||||||
if(ret<0){//Less than zero and we skipped a frame
|
if(ret<0){//Less than zero and we skipped a frame
|
||||||
|
@ -607,32 +616,37 @@ int FfmpegCamera::CaptureAndRecord( Image &image, bool recording, char* event_fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if(mConvertContext == NULL) {
|
if ( mConvertContext == NULL ) {
|
||||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
if(mConvertContext == NULL)
|
if ( mConvertContext == NULL )
|
||||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
frameCount++;
|
frameCount++;
|
||||||
}
|
} // end if frameComplete
|
||||||
}else if(packet.stream_index == mAudioStreamId){//FIXME best way to copy all other streams
|
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
|
||||||
if(videoStore && recording){
|
if ( videoStore && recording ) {
|
||||||
//Write the packet to our video store
|
if ( record_audio ) {
|
||||||
int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame
|
Debug(4, "Recording audio packet" );
|
||||||
if(ret<0){//Less than zero and we skipped a frame
|
//Write the packet to our video store
|
||||||
av_free_packet( &packet );
|
int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame
|
||||||
return 0;
|
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
||||||
}
|
av_free_packet( &packet );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(4, "Not recording audio packet" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
av_free_packet( &packet );
|
av_free_packet( &packet );
|
||||||
}
|
} // end while ! frameComplete
|
||||||
return (frameCount);
|
return (frameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ protected:
|
||||||
int64_t startTime;
|
int64_t startTime;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~FfmpegCamera();
|
~FfmpegCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
|
@ -86,7 +86,7 @@ public:
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory );
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_file_camera.h"
|
#include "zm_file_camera.h"
|
||||||
|
|
||||||
FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture )
|
FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio )
|
||||||
{
|
{
|
||||||
strncpy( path, p_path, sizeof(path) );
|
strncpy( path, p_path, sizeof(path) );
|
||||||
if ( capture )
|
if ( capture )
|
||||||
|
|
|
@ -36,7 +36,7 @@ protected:
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~FileCamera();
|
~FileCamera();
|
||||||
|
|
||||||
const char *Path() const { return( path ); }
|
const char *Path() const { return( path ); }
|
||||||
|
@ -46,7 +46,7 @@ public:
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_FILE_CAMERA_H
|
#endif // ZM_FILE_CAMERA_H
|
||||||
|
|
|
@ -61,8 +61,8 @@ void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
mMethod( p_method ),
|
mMethod( p_method ),
|
||||||
mOptions( p_options )
|
mOptions( p_options )
|
||||||
|
|
|
@ -57,7 +57,7 @@ protected:
|
||||||
libvlc_media_player_t *mLibvlcMediaPlayer;
|
libvlc_media_player_t *mLibvlcMediaPlayer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~LibvlcCamera();
|
~LibvlcCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory );
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -286,8 +286,26 @@ AVFrame **LocalCamera::capturePictures = 0;
|
||||||
|
|
||||||
LocalCamera *LocalCamera::last_camera = NULL;
|
LocalCamera *LocalCamera::last_camera = NULL;
|
||||||
|
|
||||||
LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, int p_standard, bool p_v4l_multi_buffer, unsigned int p_v4l_captures_per_frame, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras) :
|
LocalCamera::LocalCamera(
|
||||||
Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
int p_id,
|
||||||
|
const std::string &p_device,
|
||||||
|
int p_channel,
|
||||||
|
int p_standard,
|
||||||
|
bool p_v4l_multi_buffer,
|
||||||
|
unsigned int p_v4l_captures_per_frame,
|
||||||
|
const std::string &p_method,
|
||||||
|
int p_width,
|
||||||
|
int p_height,
|
||||||
|
int p_colours,
|
||||||
|
int p_palette,
|
||||||
|
int p_brightness,
|
||||||
|
int p_contrast,
|
||||||
|
int p_hue,
|
||||||
|
int p_colour,
|
||||||
|
bool p_capture,
|
||||||
|
bool p_record_audio,
|
||||||
|
unsigned int p_extras) :
|
||||||
|
Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
device( p_device ),
|
device( p_device ),
|
||||||
channel( p_channel ),
|
channel( p_channel ),
|
||||||
standard( p_standard ),
|
standard( p_standard ),
|
||||||
|
|
|
@ -116,7 +116,25 @@ protected:
|
||||||
static LocalCamera *last_camera;
|
static LocalCamera *last_camera;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0);
|
LocalCamera(
|
||||||
|
int p_id,
|
||||||
|
const std::string &device,
|
||||||
|
int p_channel,
|
||||||
|
int p_format,
|
||||||
|
bool v4lmultibuffer,
|
||||||
|
unsigned int v4lcapturesperframe,
|
||||||
|
const std::string &p_method,
|
||||||
|
int p_width,
|
||||||
|
int p_height,
|
||||||
|
int p_colours,
|
||||||
|
int p_palette,
|
||||||
|
int p_brightness,
|
||||||
|
int p_contrast,
|
||||||
|
int p_hue,
|
||||||
|
int p_colour,
|
||||||
|
bool p_capture,
|
||||||
|
bool p_record_audio,
|
||||||
|
unsigned int p_extras = 0);
|
||||||
~LocalCamera();
|
~LocalCamera();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
|
@ -138,7 +156,7 @@ public:
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
||||||
|
|
||||||
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
332
src/zm_monitor.h
332
src/zm_monitor.h
|
@ -87,7 +87,7 @@ public:
|
||||||
} State;
|
} State;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::set<Zone *> ZoneSet;
|
typedef std::set<Zone *> ZoneSet;
|
||||||
|
|
||||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||||
|
|
||||||
|
@ -96,36 +96,36 @@ protected:
|
||||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t size; /* +0 */
|
uint32_t size; /* +0 */
|
||||||
uint32_t last_write_index; /* +4 */
|
uint32_t last_write_index; /* +4 */
|
||||||
uint32_t last_read_index; /* +8 */
|
uint32_t last_read_index; /* +8 */
|
||||||
uint32_t state; /* +12 */
|
uint32_t state; /* +12 */
|
||||||
uint32_t last_event; /* +16 */
|
uint32_t last_event; /* +16 */
|
||||||
uint32_t action; /* +20 */
|
uint32_t action; /* +20 */
|
||||||
int32_t brightness; /* +24 */
|
int32_t brightness; /* +24 */
|
||||||
int32_t hue; /* +28 */
|
int32_t hue; /* +28 */
|
||||||
int32_t colour; /* +32 */
|
int32_t colour; /* +32 */
|
||||||
int32_t contrast; /* +36 */
|
int32_t contrast; /* +36 */
|
||||||
int32_t alarm_x; /* +40 */
|
int32_t alarm_x; /* +40 */
|
||||||
int32_t alarm_y; /* +44 */
|
int32_t alarm_y; /* +44 */
|
||||||
uint8_t valid; /* +48 */
|
uint8_t valid; /* +48 */
|
||||||
uint8_t active; /* +49 */
|
uint8_t active; /* +49 */
|
||||||
uint8_t signal; /* +50 */
|
uint8_t signal; /* +50 */
|
||||||
uint8_t format; /* +51 */
|
uint8_t format; /* +51 */
|
||||||
uint32_t imagesize; /* +52 */
|
uint32_t imagesize; /* +52 */
|
||||||
uint32_t epadding1; /* +56 */
|
uint32_t epadding1; /* +56 */
|
||||||
uint32_t epadding2; /* +60 */
|
uint32_t epadding2; /* +60 */
|
||||||
/*
|
/*
|
||||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||||
*/
|
*/
|
||||||
union { /* +64 */
|
union { /* +64 */
|
||||||
time_t last_write_time;
|
time_t last_write_time;
|
||||||
uint64_t extrapad1;
|
uint64_t extrapad1;
|
||||||
};
|
};
|
||||||
union { /* +72 */
|
union { /* +72 */
|
||||||
time_t last_read_time;
|
time_t last_read_time;
|
||||||
uint64_t extrapad2;
|
uint64_t extrapad2;
|
||||||
};
|
};
|
||||||
uint8_t control_state[256]; /* +80 */
|
uint8_t control_state[256]; /* +80 */
|
||||||
|
|
||||||
|
@ -134,8 +134,7 @@ protected:
|
||||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||||
|
|
||||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t trigger_state;
|
uint32_t trigger_state;
|
||||||
uint32_t trigger_score;
|
uint32_t trigger_score;
|
||||||
|
@ -146,29 +145,26 @@ protected:
|
||||||
} TriggerData;
|
} TriggerData;
|
||||||
|
|
||||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||||
struct Snapshot
|
struct Snapshot {
|
||||||
{
|
|
||||||
struct timeval *timestamp;
|
struct timeval *timestamp;
|
||||||
Image *image;
|
Image *image;
|
||||||
void* padding;
|
void* padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
|
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
|
||||||
#if 1
|
#if 1
|
||||||
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
|
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
uint32_t size;
|
||||||
uint32_t size;
|
char event_file[4096];
|
||||||
char event_file[4096];
|
uint32_t recording; //bool arch dependent so use uint32 instead
|
||||||
uint32_t recording; //bool arch dependent so use uint32 instead
|
//uint32_t frameNumber;
|
||||||
//uint32_t frameNumber;
|
|
||||||
|
} VideoStoreData;
|
||||||
} VideoStoreData;
|
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
class MonitorLink
|
class MonitorLink {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
char name[64];
|
char name[64];
|
||||||
|
@ -178,16 +174,16 @@ protected:
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
int map_fd;
|
int map_fd;
|
||||||
char mem_file[PATH_MAX];
|
char mem_file[PATH_MAX];
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
int shm_id;
|
int shm_id;
|
||||||
#endif // ZM_MEM_MAPPED
|
#endif // ZM_MEM_MAPPED
|
||||||
off_t mem_size;
|
off_t mem_size;
|
||||||
unsigned char *mem_ptr;
|
unsigned char *mem_ptr;
|
||||||
|
|
||||||
volatile SharedData *shared_data;
|
volatile SharedData *shared_data;
|
||||||
volatile TriggerData *trigger_data;
|
volatile TriggerData *trigger_data;
|
||||||
volatile VideoStoreData *video_store_data;
|
volatile VideoStoreData *video_store_data;
|
||||||
|
|
||||||
int last_state;
|
int last_state;
|
||||||
int last_event;
|
int last_event;
|
||||||
|
@ -196,21 +192,17 @@ protected:
|
||||||
MonitorLink( int p_id, const char *p_name );
|
MonitorLink( int p_id, const char *p_name );
|
||||||
~MonitorLink();
|
~MonitorLink();
|
||||||
|
|
||||||
inline int Id() const
|
inline int Id() const {
|
||||||
{
|
|
||||||
return( id );
|
return( id );
|
||||||
}
|
}
|
||||||
inline const char *Name() const
|
inline const char *Name() const {
|
||||||
{
|
|
||||||
return( name );
|
return( name );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isConnected() const
|
inline bool isConnected() const {
|
||||||
{
|
|
||||||
return( connected );
|
return( connected );
|
||||||
}
|
}
|
||||||
inline time_t getLastConnectTime() const
|
inline time_t getLastConnectTime() const {
|
||||||
{
|
|
||||||
return( last_connect_time );
|
return( last_connect_time );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,58 +219,59 @@ protected:
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
char name[64];
|
char name[64];
|
||||||
unsigned int server_id;
|
unsigned int server_id;
|
||||||
Function function; // What the monitor is doing
|
Function function; // What the monitor is doing
|
||||||
bool enabled; // Whether the monitor is enabled or asleep
|
bool enabled; // Whether the monitor is enabled or asleep
|
||||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||||
bool v4l_multi_buffer;
|
bool v4l_multi_buffer;
|
||||||
unsigned int v4l_captures_per_frame;
|
unsigned int v4l_captures_per_frame;
|
||||||
Orientation orientation; // Whether the image has to be rotated at all
|
Orientation orientation; // Whether the image has to be rotated at all
|
||||||
unsigned int deinterlacing;
|
unsigned int deinterlacing;
|
||||||
|
|
||||||
int savejpegspref;
|
int savejpegspref;
|
||||||
int videowriterpref;
|
int videowriterpref;
|
||||||
std::string encoderparams;
|
std::string encoderparams;
|
||||||
std::vector<EncoderParameter_t> encoderparamsvec;
|
std::vector<EncoderParameter_t> encoderparamsvec;
|
||||||
|
bool record_audio; // Whether to store the audio that we receive
|
||||||
|
|
||||||
int brightness; // The statically saved brightness of the camera
|
int brightness; // The statically saved brightness of the camera
|
||||||
int contrast; // The statically saved contrast of the camera
|
int contrast; // The statically saved contrast of the camera
|
||||||
int hue; // The statically saved hue of the camera
|
int hue; // The statically saved hue of the camera
|
||||||
int colour; // The statically saved colour of the camera
|
int colour; // The statically saved colour of the camera
|
||||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||||
char label_format[64]; // The format of the timestamp on the images
|
char label_format[64]; // The format of the timestamp on the images
|
||||||
Coord label_coord; // The coordinates of the timestamp on the images
|
Coord label_coord; // The coordinates of the timestamp on the images
|
||||||
int label_size; // Size of the timestamp on the images
|
int label_size; // Size of the timestamp on the images
|
||||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||||
// value is pre_event_count + alarm_frame_count - 1
|
// value is pre_event_count + alarm_frame_count - 1
|
||||||
int warmup_count; // How many images to process before looking for events
|
int warmup_count; // How many images to process before looking for events
|
||||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||||
int section_length; // How long events should last in continuous modes
|
int section_length; // How long events should last in continuous modes
|
||||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||||
int frame_skip; // How many frames to skip in continuous modes
|
int frame_skip; // How many frames to skip in continuous modes
|
||||||
int motion_frame_skip; // How many frames to skip in motion detection
|
int motion_frame_skip; // How many frames to skip in motion detection
|
||||||
double analysis_fps; // Target framerate for video analysis
|
double analysis_fps; // Target framerate for video analysis
|
||||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||||
int capture_delay; // How long we wait between capture frames
|
int capture_delay; // How long we wait between capture frames
|
||||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||||
bool track_motion; // Whether this monitor tries to track detected motion
|
bool track_motion; // Whether this monitor tries to track detected motion
|
||||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||||
|
|
||||||
double fps;
|
double fps;
|
||||||
Image delta_image;
|
Image delta_image;
|
||||||
Image ref_image;
|
Image ref_image;
|
||||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||||
Image write_image; // Used when creating snapshot images
|
Image write_image; // Used when creating snapshot images
|
||||||
|
|
||||||
Purpose purpose; // What this monitor has been created to do
|
Purpose purpose; // What this monitor has been created to do
|
||||||
int event_count;
|
int event_count;
|
||||||
int image_count;
|
int image_count;
|
||||||
int ready_count;
|
int ready_count;
|
||||||
|
@ -290,22 +283,22 @@ protected:
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
time_t last_fps_time;
|
time_t last_fps_time;
|
||||||
time_t auto_resume_time;
|
time_t auto_resume_time;
|
||||||
unsigned int last_motion_score;
|
unsigned int last_motion_score;
|
||||||
|
|
||||||
EventCloseMode event_close_mode;
|
EventCloseMode event_close_mode;
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
int map_fd;
|
int map_fd;
|
||||||
char mem_file[PATH_MAX];
|
char mem_file[PATH_MAX];
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
int shm_id;
|
int shm_id;
|
||||||
#endif // ZM_MEM_MAPPED
|
#endif // ZM_MEM_MAPPED
|
||||||
off_t mem_size;
|
off_t mem_size;
|
||||||
unsigned char *mem_ptr;
|
unsigned char *mem_ptr;
|
||||||
|
|
||||||
SharedData *shared_data;
|
SharedData *shared_data;
|
||||||
TriggerData *trigger_data;
|
TriggerData *trigger_data;
|
||||||
VideoStoreData *video_store_data;
|
VideoStoreData *video_store_data;
|
||||||
|
|
||||||
Snapshot *image_buffer;
|
Snapshot *image_buffer;
|
||||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||||
|
@ -329,54 +322,85 @@ protected:
|
||||||
public:
|
public:
|
||||||
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||||
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
||||||
Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, int p_savejpegs, int p_videowriter, std::string p_encoderparams, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
Monitor(
|
||||||
|
int p_id,
|
||||||
|
const char *p_name,
|
||||||
|
unsigned int p_server_id,
|
||||||
|
int p_function,
|
||||||
|
bool p_enabled,
|
||||||
|
const char *p_linked_monitors,
|
||||||
|
Camera *p_camera,
|
||||||
|
int p_orientation,
|
||||||
|
unsigned int p_deinterlacing,
|
||||||
|
int p_savejpegs,
|
||||||
|
int p_videowriter,
|
||||||
|
std::string p_encoderparams,
|
||||||
|
bool p_record_audio,
|
||||||
|
const char *p_event_prefix,
|
||||||
|
const char *p_label_format,
|
||||||
|
const Coord &p_label_coord,
|
||||||
|
int label_size,
|
||||||
|
int p_image_buffer_count,
|
||||||
|
int p_warmup_count,
|
||||||
|
int p_pre_event_count,
|
||||||
|
int p_post_event_count,
|
||||||
|
int p_stream_replay_buffer,
|
||||||
|
int p_alarm_frame_count,
|
||||||
|
int p_section_length,
|
||||||
|
int p_frame_skip,
|
||||||
|
int p_motion_frame_skip,
|
||||||
|
double p_analysis_fps,
|
||||||
|
unsigned int p_analysis_update_delay,
|
||||||
|
int p_capture_delay,
|
||||||
|
int p_alarm_capture_delay,
|
||||||
|
int p_fps_report_interval,
|
||||||
|
int p_ref_blend_perc,
|
||||||
|
int p_alarm_ref_blend_perc,
|
||||||
|
bool p_track_motion,
|
||||||
|
Rgb p_signal_check_colour,
|
||||||
|
bool p_embed_exif,
|
||||||
|
Purpose p_purpose,
|
||||||
|
int p_n_zones=0,
|
||||||
|
Zone *p_zones[]=0
|
||||||
|
);
|
||||||
~Monitor();
|
~Monitor();
|
||||||
|
|
||||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||||
|
|
||||||
bool connect();
|
bool connect();
|
||||||
inline int ShmValid() const
|
inline int ShmValid() const {
|
||||||
{
|
|
||||||
return( shared_data->valid );
|
return( shared_data->valid );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Id() const
|
inline int Id() const {
|
||||||
{
|
|
||||||
return( id );
|
return( id );
|
||||||
}
|
}
|
||||||
inline const char *Name() const
|
inline const char *Name() const {
|
||||||
{
|
|
||||||
return( name );
|
return( name );
|
||||||
}
|
}
|
||||||
inline Function GetFunction() const
|
inline Function GetFunction() const {
|
||||||
{
|
|
||||||
return( function );
|
return( function );
|
||||||
}
|
}
|
||||||
inline bool Enabled()
|
inline bool Enabled() {
|
||||||
{
|
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( enabled );
|
return( enabled );
|
||||||
}
|
}
|
||||||
inline const char *EventPrefix() const
|
inline const char *EventPrefix() const {
|
||||||
{
|
|
||||||
return( event_prefix );
|
return( event_prefix );
|
||||||
}
|
}
|
||||||
inline bool Ready()
|
inline bool Ready() {
|
||||||
{
|
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( image_count > ready_count );
|
return( image_count > ready_count );
|
||||||
}
|
}
|
||||||
inline bool Active()
|
inline bool Active() {
|
||||||
{
|
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( enabled && shared_data->active );
|
return( enabled && shared_data->active );
|
||||||
}
|
}
|
||||||
inline bool Exif()
|
inline bool Exif() {
|
||||||
{
|
|
||||||
return( embed_exif );
|
return( embed_exif );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +411,7 @@ public:
|
||||||
|
|
||||||
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
||||||
int GetOptVideoWriter() const { return( videowriterpref ); }
|
int GetOptVideoWriter() const { return( videowriterpref ); }
|
||||||
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
||||||
|
|
||||||
State GetState() const;
|
State GetState() const;
|
||||||
int GetImage( int index=-1, int scale=100 );
|
int GetImage( int index=-1, int scale=100 );
|
||||||
|
@ -417,17 +441,14 @@ public:
|
||||||
int actionColour( int p_colour=-1 );
|
int actionColour( int p_colour=-1 );
|
||||||
int actionContrast( int p_contrast=-1 );
|
int actionContrast( int p_contrast=-1 );
|
||||||
|
|
||||||
inline int PrimeCapture()
|
inline int PrimeCapture() {
|
||||||
{
|
|
||||||
return( camera->PrimeCapture() );
|
return( camera->PrimeCapture() );
|
||||||
}
|
}
|
||||||
inline int PreCapture()
|
inline int PreCapture() {
|
||||||
{
|
|
||||||
return( camera->PreCapture() );
|
return( camera->PreCapture() );
|
||||||
}
|
}
|
||||||
int Capture();
|
int Capture();
|
||||||
int PostCapture()
|
int PostCapture() {
|
||||||
{
|
|
||||||
return( camera->PostCapture() );
|
return( camera->PostCapture() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +477,7 @@ public:
|
||||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||||
|
@ -470,53 +491,48 @@ public:
|
||||||
|
|
||||||
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))
|
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))
|
||||||
|
|
||||||
class MonitorStream : public StreamBase
|
class MonitorStream : public StreamBase {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
typedef struct SwapImage {
|
typedef struct SwapImage {
|
||||||
bool valid;
|
bool valid;
|
||||||
struct timeval timestamp;
|
struct timeval timestamp;
|
||||||
char file_name[PATH_MAX];
|
char file_name[PATH_MAX];
|
||||||
} SwapImage;
|
} SwapImage;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SwapImage *temp_image_buffer;
|
SwapImage *temp_image_buffer;
|
||||||
int temp_image_buffer_count;
|
int temp_image_buffer_count;
|
||||||
int temp_read_index;
|
int temp_read_index;
|
||||||
int temp_write_index;
|
int temp_write_index;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
time_t ttl;
|
time_t ttl;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int playback_buffer;
|
int playback_buffer;
|
||||||
bool delayed;
|
bool delayed;
|
||||||
|
|
||||||
int frame_count;
|
int frame_count;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool checkSwapPath( const char *path, bool create_path );
|
bool checkSwapPath( const char *path, bool create_path );
|
||||||
|
|
||||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||||
void processCommand( const CmdMsg *msg );
|
void processCommand( const CmdMsg *msg );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 )
|
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) {
|
||||||
{
|
}
|
||||||
}
|
void setStreamBuffer( int p_playback_buffer ) {
|
||||||
void setStreamBuffer( int p_playback_buffer )
|
playback_buffer = p_playback_buffer;
|
||||||
{
|
}
|
||||||
playback_buffer = p_playback_buffer;
|
void setStreamTTL( time_t p_ttl ) {
|
||||||
}
|
ttl = p_ttl;
|
||||||
void setStreamTTL( time_t p_ttl )
|
}
|
||||||
{
|
bool setStreamStart( int monitor_id ) {
|
||||||
ttl = p_ttl;
|
return loadMonitor( monitor_id );
|
||||||
}
|
}
|
||||||
bool setStreamStart( int monitor_id )
|
|
||||||
{
|
|
||||||
return loadMonitor( monitor_id );
|
|
||||||
}
|
|
||||||
void runStream();
|
void runStream();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,23 @@
|
||||||
|
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
|
|
||||||
RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
RemoteCamera::RemoteCamera(
|
||||||
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
int p_id,
|
||||||
|
const std::string &p_protocol,
|
||||||
|
const std::string &p_host,
|
||||||
|
const std::string &p_port,
|
||||||
|
const std::string &p_path,
|
||||||
|
int p_width,
|
||||||
|
int p_height,
|
||||||
|
int p_colours,
|
||||||
|
int p_brightness,
|
||||||
|
int p_contrast,
|
||||||
|
int p_hue,
|
||||||
|
int p_colour,
|
||||||
|
bool p_capture,
|
||||||
|
bool p_record_audio
|
||||||
|
) :
|
||||||
|
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
protocol( p_protocol ),
|
protocol( p_protocol ),
|
||||||
host( p_host ),
|
host( p_host ),
|
||||||
port( p_port ),
|
port( p_port ),
|
||||||
|
|
|
@ -55,7 +55,22 @@ protected:
|
||||||
struct addrinfo *hp;
|
struct addrinfo *hp;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCamera(
|
||||||
|
int p_id,
|
||||||
|
const std::string &p_proto,
|
||||||
|
const std::string &p_host,
|
||||||
|
const std::string &p_port,
|
||||||
|
const std::string &p_path,
|
||||||
|
int p_width,
|
||||||
|
int p_height,
|
||||||
|
int p_colours,
|
||||||
|
int p_brightness,
|
||||||
|
int p_contrast,
|
||||||
|
int p_hue,
|
||||||
|
int p_colour,
|
||||||
|
bool p_capture,
|
||||||
|
bool p_record_audio
|
||||||
|
);
|
||||||
virtual ~RemoteCamera();
|
virtual ~RemoteCamera();
|
||||||
|
|
||||||
const std::string &Protocol() const { return( protocol ); }
|
const std::string &Protocol() const { return( protocol ); }
|
||||||
|
@ -73,7 +88,7 @@ public:
|
||||||
virtual int PreCapture() = 0;
|
virtual int PreCapture() = 0;
|
||||||
virtual int Capture( Image &image ) = 0;
|
virtual int Capture( Image &image ) = 0;
|
||||||
virtual int PostCapture() = 0;
|
virtual int PostCapture() = 0;
|
||||||
virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory )=0;
|
virtual int CaptureAndRecord( Image &image, bool recording, char* event_directory )=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_REMOTE_CAMERA_H
|
#endif // ZM_REMOTE_CAMERA_H
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
#include <sys/filio.h> // FIONREAD and friends
|
#include <sys/filio.h> // FIONREAD and friends
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture )
|
RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio )
|
||||||
{
|
{
|
||||||
sd = -1;
|
sd = -1;
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ protected:
|
||||||
enum { SIMPLE, REGEXP } method;
|
enum { SIMPLE, REGEXP } method;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~RemoteCameraHttp();
|
~RemoteCameraHttp();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
|
@ -58,7 +58,7 @@ public:
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
||||||
|
|
|
@ -28,22 +28,22 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||||
rtsp_describe( p_rtsp_describe ),
|
rtsp_describe( p_rtsp_describe ),
|
||||||
rtspThread( 0 )
|
rtspThread( 0 )
|
||||||
|
|
||||||
{
|
{
|
||||||
if ( p_method == "rtpUni" )
|
if ( p_method == "rtpUni" )
|
||||||
method = RtspThread::RTP_UNICAST;
|
method = RtspThread::RTP_UNICAST;
|
||||||
else if ( p_method == "rtpMulti" )
|
else if ( p_method == "rtpMulti" )
|
||||||
method = RtspThread::RTP_MULTICAST;
|
method = RtspThread::RTP_MULTICAST;
|
||||||
else if ( p_method == "rtpRtsp" )
|
else if ( p_method == "rtpRtsp" )
|
||||||
method = RtspThread::RTP_RTSP;
|
method = RtspThread::RTP_RTSP;
|
||||||
else if ( p_method == "rtpRtspHttp" )
|
else if ( p_method == "rtpRtspHttp" )
|
||||||
method = RtspThread::RTP_RTSP_HTTP;
|
method = RtspThread::RTP_RTSP_HTTP;
|
||||||
else
|
else
|
||||||
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
|
@ -52,13 +52,16 @@ RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const
|
||||||
|
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
|
mAudioStreamId = -1;
|
||||||
mCodecContext = NULL;
|
mCodecContext = NULL;
|
||||||
mCodec = NULL;
|
mCodec = NULL;
|
||||||
mRawFrame = NULL;
|
mRawFrame = NULL;
|
||||||
mFrame = NULL;
|
mFrame = NULL;
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
|
wasRecording = false;
|
||||||
|
startTime=0;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||||
|
@ -80,26 +83,26 @@ RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const
|
||||||
RemoteCameraRtsp::~RemoteCameraRtsp()
|
RemoteCameraRtsp::~RemoteCameraRtsp()
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
av_frame_free( &mFrame );
|
av_frame_free( &mFrame );
|
||||||
av_frame_free( &mRawFrame );
|
av_frame_free( &mRawFrame );
|
||||||
#else
|
#else
|
||||||
av_freep( &mFrame );
|
av_freep( &mFrame );
|
||||||
av_freep( &mRawFrame );
|
av_freep( &mRawFrame );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if ( mConvertContext )
|
if ( mConvertContext )
|
||||||
{
|
{
|
||||||
sws_freeContext( mConvertContext );
|
sws_freeContext( mConvertContext );
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( mCodecContext )
|
if ( mCodecContext )
|
||||||
{
|
{
|
||||||
avcodec_close( mCodecContext );
|
avcodec_close( mCodecContext );
|
||||||
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
|
@ -109,106 +112,109 @@ RemoteCameraRtsp::~RemoteCameraRtsp()
|
||||||
|
|
||||||
void RemoteCameraRtsp::Initialise()
|
void RemoteCameraRtsp::Initialise()
|
||||||
{
|
{
|
||||||
RemoteCamera::Initialise();
|
RemoteCamera::Initialise();
|
||||||
|
|
||||||
int max_size = width*height*colours;
|
int max_size = width*height*colours;
|
||||||
|
|
||||||
|
// This allocates a buffer able to hold a raw fframe, which is a little artbitrary. Might be nice to get some
|
||||||
|
// decent data on how large a buffer is really needed.
|
||||||
buffer.size( max_size );
|
buffer.size( max_size );
|
||||||
|
|
||||||
if ( logDebugging() )
|
if ( logDebugging() )
|
||||||
av_log_set_level( AV_LOG_DEBUG );
|
av_log_set_level( AV_LOG_DEBUG );
|
||||||
else
|
else
|
||||||
av_log_set_level( AV_LOG_QUIET );
|
av_log_set_level( AV_LOG_QUIET );
|
||||||
|
|
||||||
av_register_all();
|
av_register_all();
|
||||||
|
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteCameraRtsp::Terminate()
|
void RemoteCameraRtsp::Terminate()
|
||||||
{
|
{
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Connect()
|
int RemoteCameraRtsp::Connect()
|
||||||
{
|
{
|
||||||
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
||||||
|
|
||||||
rtspThread->start();
|
rtspThread->start();
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Disconnect()
|
int RemoteCameraRtsp::Disconnect()
|
||||||
{
|
{
|
||||||
if ( rtspThread )
|
if ( rtspThread )
|
||||||
{
|
{
|
||||||
rtspThread->stop();
|
rtspThread->stop();
|
||||||
rtspThread->join();
|
rtspThread->join();
|
||||||
delete rtspThread;
|
delete rtspThread;
|
||||||
rtspThread = 0;
|
rtspThread = 0;
|
||||||
}
|
}
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
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" );
|
Fatal( "No RTSP sources" );
|
||||||
|
|
||||||
Debug( 2, "Got sources" );
|
Debug( 2, "Got sources" );
|
||||||
|
|
||||||
mFormatContext = rtspThread->getFormatContext();
|
mFormatContext = rtspThread->getFormatContext();
|
||||||
|
|
||||||
// Find first video stream present
|
// Find first video stream present
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
// Find the first video stream.
|
||||||
|
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||||
#else
|
#else
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
mVideoStreamId = i;
|
mVideoStreamId = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( mVideoStreamId == -1 )
|
if ( mVideoStreamId == -1 )
|
||||||
Fatal( "Unable to locate video stream" );
|
Fatal( "Unable to locate video stream" );
|
||||||
|
|
||||||
// Get a pointer to the codec context for the video stream
|
// Get a pointer to the codec context for the video stream
|
||||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||||
|
|
||||||
// Find the decoder for the video stream
|
// Find the decoder for the video stream
|
||||||
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
||||||
if ( mCodec == NULL )
|
if ( mCodec == NULL )
|
||||||
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
||||||
|
|
||||||
// Open codec
|
// Open codec
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||||
#else
|
#else
|
||||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||||
#endif
|
#endif
|
||||||
Panic( "Can't open codec" );
|
Panic( "Can't open codec" );
|
||||||
|
|
||||||
// Allocate space for the native video frame
|
// Allocate space for the native video frame
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
mRawFrame = av_frame_alloc();
|
mRawFrame = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
mRawFrame = avcodec_alloc_frame();
|
mRawFrame = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Allocate space for the converted video frame
|
// Allocate space for the converted video frame
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
mFrame = av_frame_alloc();
|
mFrame = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
mFrame = avcodec_alloc_frame();
|
mFrame = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(mRawFrame == NULL || mFrame == NULL)
|
if(mRawFrame == NULL || mFrame == NULL)
|
||||||
|
@ -229,27 +235,25 @@ int RemoteCameraRtsp::PrimeCapture()
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::PreCapture()
|
int RemoteCameraRtsp::PreCapture() {
|
||||||
{
|
if ( !rtspThread->isRunning() )
|
||||||
if ( !rtspThread->isRunning() )
|
return( -1 );
|
||||||
return( -1 );
|
if ( !rtspThread->hasSources() )
|
||||||
if ( !rtspThread->hasSources() )
|
{
|
||||||
{
|
Error( "Cannot precapture, no RTP sources" );
|
||||||
Error( "Cannot precapture, no RTP sources" );
|
return( -1 );
|
||||||
return( -1 );
|
}
|
||||||
}
|
return( 0 );
|
||||||
return( 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Capture( Image &image )
|
int RemoteCameraRtsp::Capture( Image &image ) {
|
||||||
{
|
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
uint8_t* directbuffer;
|
uint8_t* directbuffer;
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
|
@ -261,111 +265,279 @@ int RemoteCameraRtsp::Capture( Image &image )
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( true )
|
while ( true ) {
|
||||||
{
|
buffer.clear();
|
||||||
buffer.clear();
|
if ( !rtspThread->isRunning() )
|
||||||
if ( !rtspThread->isRunning() )
|
return (-1);
|
||||||
return (-1);
|
|
||||||
|
|
||||||
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() );
|
||||||
Debug( 4, "Address %p", buffer.head() );
|
Hexdump( 4, buffer.head(), 16 );
|
||||||
Hexdump( 4, buffer.head(), 16 );
|
|
||||||
|
|
||||||
if ( !buffer.size() )
|
if ( !buffer.size() )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
if(mCodecContext->codec_id == AV_CODEC_ID_H264)
|
if(mCodecContext->codec_id == AV_CODEC_ID_H264) {
|
||||||
{
|
// SPS and PPS frames should be saved and appended to IDR frames
|
||||||
// SPS and PPS frames should be saved and appended to IDR frames
|
int nalType = (buffer.head()[3] & 0x1f);
|
||||||
int nalType = (buffer.head()[3] & 0x1f);
|
|
||||||
|
// SPS The SPS NAL unit contains parameters that apply to a series of consecutive coded video pictures
|
||||||
// SPS
|
if(nalType == 7)
|
||||||
if(nalType == 7)
|
{
|
||||||
{
|
lastSps = buffer;
|
||||||
lastSps = buffer;
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
// PPS The PPS NAL unit contains parameters that apply to the decoding of one or more individual pictures inside a coded video sequence
|
||||||
// PPS
|
else if(nalType == 8)
|
||||||
else if(nalType == 8)
|
{
|
||||||
{
|
lastPps = buffer;
|
||||||
lastPps = buffer;
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
// IDR
|
||||||
// IDR
|
else if(nalType == 5)
|
||||||
else if(nalType == 5)
|
{
|
||||||
{
|
buffer += lastSps;
|
||||||
buffer += lastSps;
|
buffer += lastPps;
|
||||||
buffer += lastPps;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
av_init_packet( &packet );
|
av_init_packet( &packet );
|
||||||
|
|
||||||
while ( !frameComplete && buffer.size() > 0 )
|
|
||||||
{
|
|
||||||
packet.data = buffer.head();
|
|
||||||
packet.size = buffer.size();
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
|
||||||
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
|
||||||
#else
|
|
||||||
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
|
||||||
#endif
|
|
||||||
if ( len < 0 )
|
|
||||||
{
|
|
||||||
Error( "Error while decoding frame %d", frameCount );
|
|
||||||
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
|
||||||
buffer.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
|
||||||
//if ( buffer.size() < 400 )
|
|
||||||
//Hexdump( 0, buffer.head(), buffer.size() );
|
|
||||||
|
|
||||||
buffer -= len;
|
|
||||||
|
|
||||||
}
|
|
||||||
if ( frameComplete ) {
|
|
||||||
|
|
||||||
Debug( 3, "Got frame %d", frameCount );
|
|
||||||
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
while ( !frameComplete && buffer.size() > 0 ) {
|
||||||
if(mConvertContext == NULL) {
|
packet.data = buffer.head();
|
||||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
packet.size = buffer.size();
|
||||||
|
|
||||||
if(mConvertContext == NULL)
|
// So I think this is the magic decode step. Result is a raw image?
|
||||||
Fatal( "Unable to create conversion context");
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
}
|
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
||||||
|
#else
|
||||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
||||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
#endif
|
||||||
#else // HAVE_LIBSWSCALE
|
if ( len < 0 ) {
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
Error( "Error while decoding frame %d", frameCount );
|
||||||
#endif // HAVE_LIBSWSCALE
|
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
||||||
|
buffer.clear();
|
||||||
frameCount++;
|
continue;
|
||||||
|
}
|
||||||
|
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
||||||
|
//if ( buffer.size() < 400 )
|
||||||
|
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||||
|
|
||||||
|
buffer -= len;
|
||||||
|
}
|
||||||
|
// At this point, we either have a frame or ran out of buffer. What happens if we run out of buffer?
|
||||||
|
if ( frameComplete ) {
|
||||||
|
|
||||||
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height );
|
||||||
|
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
if(mConvertContext == NULL) {
|
||||||
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
|
|
||||||
} /* frame complete */
|
if(mConvertContext == NULL)
|
||||||
|
Fatal( "Unable to create conversion context");
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
}
|
||||||
av_packet_unref( &packet);
|
|
||||||
#else
|
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||||
av_free_packet( &packet );
|
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||||
#endif
|
#else // HAVE_LIBSWSCALE
|
||||||
} /* getFrame() */
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||||
|
#endif // HAVE_LIBSWSCALE
|
||||||
if(frameComplete)
|
|
||||||
return (0);
|
frameCount++;
|
||||||
|
|
||||||
|
} /* frame complete */
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||||
|
av_packet_unref( &packet );
|
||||||
|
#else
|
||||||
|
av_free_packet( &packet );
|
||||||
|
#endif
|
||||||
|
} /* getFrame() */
|
||||||
|
|
||||||
|
if(frameComplete)
|
||||||
|
return (0);
|
||||||
|
|
||||||
}
|
} // end while true
|
||||||
return (0) ;
|
|
||||||
|
// can never get here.
|
||||||
|
return (0) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Function to handle capture and store
|
||||||
|
int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* event_file ) {
|
||||||
|
AVPacket packet;
|
||||||
|
uint8_t* directbuffer;
|
||||||
|
int frameComplete = false;
|
||||||
|
|
||||||
|
/* Request a writeable buffer of the target image */
|
||||||
|
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
|
if(directbuffer == NULL) {
|
||||||
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( true ) {
|
||||||
|
buffer.clear();
|
||||||
|
if ( !rtspThread->isRunning() )
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if ( rtspThread->getFrame( buffer ) ) {
|
||||||
|
Debug( 3, "Read frame %d bytes", buffer.size() );
|
||||||
|
Debug( 4, "Address %p", buffer.head() );
|
||||||
|
Hexdump( 4, buffer.head(), 16 );
|
||||||
|
|
||||||
|
if ( !buffer.size() )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if(mCodecContext->codec_id == AV_CODEC_ID_H264) {
|
||||||
|
// SPS and PPS frames should be saved and appended to IDR frames
|
||||||
|
int nalType = (buffer.head()[3] & 0x1f);
|
||||||
|
|
||||||
|
// SPS
|
||||||
|
if(nalType == 7) {
|
||||||
|
lastSps = buffer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// PPS
|
||||||
|
else if(nalType == 8) {
|
||||||
|
lastPps = buffer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// IDR
|
||||||
|
else if(nalType == 5) {
|
||||||
|
buffer += lastSps;
|
||||||
|
buffer += lastPps;
|
||||||
|
}
|
||||||
|
} // end if H264, what about other codecs?
|
||||||
|
|
||||||
|
av_init_packet( &packet );
|
||||||
|
|
||||||
|
// Why are we checking for it being the video stream
|
||||||
|
if ( packet.stream_index == mVideoStreamId ) {
|
||||||
|
|
||||||
|
while ( !frameComplete && buffer.size() > 0 ) {
|
||||||
|
packet.data = buffer.head();
|
||||||
|
packet.size = buffer.size();
|
||||||
|
|
||||||
|
// So this does the decode
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
|
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
||||||
|
#else
|
||||||
|
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
||||||
|
#endif
|
||||||
|
if ( len < 0 ) {
|
||||||
|
Error( "Error while decoding frame %d", frameCount );
|
||||||
|
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
||||||
|
buffer.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
||||||
|
//if ( buffer.size() < 400 )
|
||||||
|
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||||
|
|
||||||
|
buffer -= len;
|
||||||
|
} // end while get & decode a frame
|
||||||
|
|
||||||
|
if ( frameComplete ) {
|
||||||
|
|
||||||
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height );
|
||||||
|
|
||||||
|
//Video recording
|
||||||
|
if ( recording && !wasRecording ) {
|
||||||
|
//Instantiate the video storage module
|
||||||
|
|
||||||
|
videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime);
|
||||||
|
wasRecording = true;
|
||||||
|
strcpy(oldDirectory, event_file);
|
||||||
|
|
||||||
|
} else if ( !recording && wasRecording && videoStore ) {
|
||||||
|
// Why are we deleting the videostore? Becase for soem reason we are no longer recording? How does that happen?
|
||||||
|
Info("Deleting videoStore instance");
|
||||||
|
delete videoStore;
|
||||||
|
videoStore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//The directory we are recording to is no longer tied to the current event. Need to re-init the videostore with the correct directory and start recording again
|
||||||
|
if ( recording && wasRecording && (strcmp(oldDirectory, event_file)!=0) && (packet.flags & AV_PKT_FLAG_KEY) ) {
|
||||||
|
//don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?...if we store our key frame location with the event will that be enough?
|
||||||
|
Info("Re-starting video storage module");
|
||||||
|
if ( videoStore ) {
|
||||||
|
delete videoStore;
|
||||||
|
videoStore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId],mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],startTime);
|
||||||
|
strcpy( oldDirectory, event_file );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( videoStore && recording ) {
|
||||||
|
//Write the packet to our video store
|
||||||
|
int ret = videoStore->writeVideoFramePacket(&packet, mFormatContext->streams[mVideoStreamId]);//, &lastKeyframePkt);
|
||||||
|
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
||||||
|
av_free_packet( &packet );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
if(mConvertContext == NULL) {
|
||||||
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
|
|
||||||
|
if(mConvertContext == NULL)
|
||||||
|
Fatal( "Unable to create conversion context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||||
|
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||||
|
#else // HAVE_LIBSWSCALE
|
||||||
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||||
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
|
||||||
|
} /* frame complete */
|
||||||
|
} else if ( packet.stream_index == mAudioStreamId ) {
|
||||||
|
Debug( 4, "Got audio packet" );
|
||||||
|
if ( videoStore && recording ) {
|
||||||
|
if ( record_audio ) {
|
||||||
|
Debug( 4, "Storing Audio packet" );
|
||||||
|
//Write the packet to our video store
|
||||||
|
int ret = videoStore->writeAudioFramePacket(&packet, mFormatContext->streams[packet.stream_index]); //FIXME no relevance of last key frame
|
||||||
|
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||||
|
av_packet_unref( &packet );
|
||||||
|
#else
|
||||||
|
av_free_packet( &packet );
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug( 4, "Not storing audio" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end if video or audio packet
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||||
|
av_packet_unref( &packet );
|
||||||
|
#else
|
||||||
|
av_free_packet( &packet );
|
||||||
|
#endif
|
||||||
|
} /* getFrame() */
|
||||||
|
|
||||||
|
if(frameComplete)
|
||||||
|
return (0);
|
||||||
|
} // end while true
|
||||||
|
return (0) ;
|
||||||
|
} // int RemoteCameraRtsp::CaptureAndRecord( Image &image, bool recording, char* event_file )
|
||||||
|
|
||||||
int RemoteCameraRtsp::PostCapture()
|
int RemoteCameraRtsp::PostCapture()
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_rtsp.h"
|
#include "zm_rtsp.h"
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
|
#include "zm_videostore.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Class representing 'rtsp' cameras, i.e. those which are
|
// Class representing 'rtsp' cameras, i.e. those which are
|
||||||
|
@ -40,11 +41,11 @@ protected:
|
||||||
int rtsp_sd;
|
int rtsp_sd;
|
||||||
int rtp_sd;
|
int rtp_sd;
|
||||||
int rtcp_sd;
|
int rtcp_sd;
|
||||||
bool rtsp_describe;
|
bool rtsp_describe;
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Buffer lastSps;
|
Buffer lastSps;
|
||||||
Buffer lastPps;
|
Buffer lastPps;
|
||||||
|
|
||||||
RtspThread::RtspMethod method;
|
RtspThread::RtspMethod method;
|
||||||
|
|
||||||
|
@ -55,31 +56,36 @@ protected:
|
||||||
#if HAVE_LIBAVFORMAT
|
#if HAVE_LIBAVFORMAT
|
||||||
AVFormatContext *mFormatContext;
|
AVFormatContext *mFormatContext;
|
||||||
int mVideoStreamId;
|
int mVideoStreamId;
|
||||||
|
int mAudioStreamId;
|
||||||
AVCodecContext *mCodecContext;
|
AVCodecContext *mCodecContext;
|
||||||
AVCodec *mCodec;
|
AVCodec *mCodec;
|
||||||
AVFrame *mRawFrame;
|
AVFrame *mRawFrame;
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
bool wasRecording;
|
||||||
|
VideoStore *videoStore;
|
||||||
|
char oldDirectory[4096];
|
||||||
|
int64_t startTime;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext;
|
struct SwsContext *mConvertContext;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||||
~RemoteCameraRtsp();
|
~RemoteCameraRtsp();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
int Connect();
|
int Connect();
|
||||||
int Disconnect();
|
int Disconnect();
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
int CaptureAndRecord( Image &image, bool recording, char* event_directory ) {return(0);};
|
int CaptureAndRecord( Image &image, bool recording, char* event_directory );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_REMOTE_CAMERA_RTSP_H
|
#endif // ZM_REMOTE_CAMERA_RTSP_H
|
||||||
|
|
|
@ -262,6 +262,11 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||||
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
||||||
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
||||||
|
// M stands for Market, it is the 8th bit
|
||||||
|
// The interpretation of the marker is defined by a profile. It is intended
|
||||||
|
// to allow significant events such as frame boundaries to be marked in the
|
||||||
|
// packet stream. A profile may define additional marker bits or specify
|
||||||
|
// that there is no marker bit by changing the number of bits in the payload type field.
|
||||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||||
|
|
||||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||||
|
@ -275,15 +280,18 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||||
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 );
|
||||||
|
|
||||||
switch (nalType)
|
switch (nalType)
|
||||||
{
|
{
|
||||||
case 24:
|
case 24: // STAP-A
|
||||||
{
|
{
|
||||||
extraHeader = 2;
|
extraHeader = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 25: case 26: case 27:
|
case 25: // STAP-B
|
||||||
|
case 26: // MTAP-16
|
||||||
|
case 27: // MTAP-24
|
||||||
{
|
{
|
||||||
extraHeader = 3;
|
extraHeader = 3;
|
||||||
break;
|
break;
|
||||||
|
@ -304,6 +312,9 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||||
extraHeader = 2;
|
extraHeader = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default: {
|
||||||
|
Debug(3, "Unhandled nalType %d", nalType );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append NAL frame start code
|
// Append NAL frame start code
|
||||||
|
@ -311,6 +322,8 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||||
mFrame.append( "\x0\x0\x1", 3 );
|
mFrame.append( "\x0\x0\x1", 3 );
|
||||||
}
|
}
|
||||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||||
|
} else {
|
||||||
|
Debug( 3, "NOT H264 frame: type is %d", mCodecId );
|
||||||
}
|
}
|
||||||
|
|
||||||
Hexdump( 4, mFrame.head(), 16 );
|
Hexdump( 4, mFrame.head(), 16 );
|
||||||
|
|
|
@ -64,7 +64,7 @@ X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, con
|
||||||
if(zm_pf == 0) {
|
if(zm_pf == 0) {
|
||||||
Error("Unable to match ffmpeg pixelformat");
|
Error("Unable to match ffmpeg pixelformat");
|
||||||
}
|
}
|
||||||
codec_pf = AV_PIX_FMT_YUV420P;
|
codec_pf = PIX_FMT_YUV420P;
|
||||||
|
|
||||||
swscaleobj.SetDefaults(zm_pf, codec_pf, width, height);
|
swscaleobj.SetDefaults(zm_pf, codec_pf, width, height);
|
||||||
|
|
||||||
|
|
|
@ -120,8 +120,8 @@ protected:
|
||||||
|
|
||||||
/* SWScale */
|
/* SWScale */
|
||||||
SWScale swscaleobj;
|
SWScale swscaleobj;
|
||||||
enum _AVPIXELFORMAT zm_pf;
|
enum PixelFormat zm_pf;
|
||||||
enum _AVPIXELFORMAT codec_pf;
|
enum PixelFormat codec_pf;
|
||||||
size_t codec_imgsize;
|
size_t codec_imgsize;
|
||||||
size_t zm_imgsize;
|
size_t zm_imgsize;
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Define the line ending behavior of the different file extensions
|
|
||||||
# Set default behaviour, in case users don't have core.autocrlf set.
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
# Explicitly declare text files we want to always be normalized and converted
|
|
||||||
# to native line endings on checkout.
|
|
||||||
*.php text
|
|
||||||
*.default text
|
|
||||||
*.ctp text
|
|
||||||
*.sql text
|
|
||||||
*.md text
|
|
||||||
*.po text
|
|
||||||
*.js text
|
|
||||||
*.css text
|
|
||||||
*.ini text
|
|
||||||
*.properties text
|
|
||||||
*.txt text
|
|
||||||
*.xml text
|
|
||||||
*.yml text
|
|
||||||
.htaccess text
|
|
||||||
|
|
||||||
# Declare files that will always have CRLF line endings on checkout.
|
|
||||||
*.bat eol=crlf
|
|
||||||
|
|
||||||
# Declare files that will always have LF line endings on checkout.
|
|
||||||
*.pem eol=lf
|
|
||||||
|
|
||||||
# Denote all files that are truly binary and should not be modified.
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.gif binary
|
|
||||||
*.ico binary
|
|
||||||
*.mo binary
|
|
|
@ -1,21 +0,0 @@
|
||||||
# User specific & automatically generated files #
|
|
||||||
#################################################
|
|
||||||
/app/Config/database.php
|
|
||||||
/app/tmp
|
|
||||||
/lib/Cake/Console/Templates/skel/tmp/
|
|
||||||
/plugins
|
|
||||||
/vendors
|
|
||||||
/build
|
|
||||||
/dist
|
|
||||||
/tags
|
|
||||||
|
|
||||||
# OS generated files #
|
|
||||||
######################
|
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
Icon?
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
|
@ -1153,30 +1153,38 @@ function zmaCheck( $monitor )
|
||||||
|
|
||||||
function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false )
|
function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false )
|
||||||
{
|
{
|
||||||
$eventPath = getEventPath( $event );
|
$eventPath = ZM_DIR_EVENTS.'/'.getEventPath( $event );
|
||||||
|
|
||||||
if ( !is_array($frame) )
|
if ( !is_array($frame) )
|
||||||
$frame = array( 'FrameId'=>$frame, 'Type'=>'' );
|
$frame = array( 'FrameId'=>$frame, 'Type'=>'' );
|
||||||
|
|
||||||
//echo "S:$scale, CO:$captureOnly<br>";
|
if ( file_exists( $eventPath.'/snapshot.jpg' ) ) {
|
||||||
$currEvent = dbFetchOne( 'SELECT M.SaveJPEGs FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = '.$event['Id'] );
|
|
||||||
if ( $currEvent['SaveJPEGs'] == "4" )
|
|
||||||
$captImage = "snapshot.jpg";
|
$captImage = "snapshot.jpg";
|
||||||
else
|
} else {
|
||||||
$captImage = sprintf( "%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", $frame['FrameId'] );
|
$captImage = sprintf( "%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", $frame['FrameId'] );
|
||||||
|
if ( ! file_exists( $eventPath.'/'.$captImage ) ) {
|
||||||
|
# Generate the frame JPG
|
||||||
|
if ( $event['DefaultVideo'] ) {
|
||||||
|
$command ='ffmpeg -v 0 -i '.$eventPath.'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$frame['FrameId'].'),setpts=PTS-STARTPTS" '.$eventPath.'/'.$captImage;
|
||||||
|
system( $command, $output, $retval );
|
||||||
|
} else {
|
||||||
|
Error("Can't create frame images from video becuase there is no video file for this event " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$captPath = $eventPath.'/'.$captImage;
|
$captPath = $eventPath.'/'.$captImage;
|
||||||
$thumbCaptPath = ZM_DIR_IMAGES.'/'.$event['Id'].'-'.$captImage;
|
$thumbCaptPath = ZM_DIR_IMAGES.'/'.$event['Id'].'-'.$captImage;
|
||||||
//echo "CI:$captImage, CP:$captPath, TCP:$thumbCaptPath<br>";
|
//echo "CI:$captImage, CP:$captPath, TCP:$thumbCaptPath<br>";
|
||||||
|
|
||||||
$analImage = sprintf( "%0".ZM_EVENT_IMAGE_DIGITS."d-analyse.jpg", $frame['FrameId'] );
|
$analImage = sprintf( "%0".ZM_EVENT_IMAGE_DIGITS."d-analyse.jpg", $frame['FrameId'] );
|
||||||
$analPath = $eventPath.'/'.$analImage;
|
$analPath = $eventPath.'/'.$analImage;
|
||||||
$analFile = ZM_DIR_EVENTS."/".$analPath;
|
|
||||||
$thumbAnalPath = ZM_DIR_IMAGES.'/'.$event['Id'].'-'.$analImage;
|
$thumbAnalPath = ZM_DIR_IMAGES.'/'.$event['Id'].'-'.$analImage;
|
||||||
//echo "AI:$analImage, AP:$analPath, TAP:$thumbAnalPath<br>";
|
//echo "AI:$analImage, AP:$analPath, TAP:$thumbAnalPath<br>";
|
||||||
|
|
||||||
$alarmFrame = $frame['Type']=='Alarm';
|
$alarmFrame = $frame['Type']=='Alarm';
|
||||||
|
|
||||||
$hasAnalImage = $alarmFrame && file_exists( $analFile ) && filesize( $analFile );
|
$hasAnalImage = $alarmFrame && file_exists( $analPath ) && filesize( $analPath );
|
||||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
$isAnalImage = $hasAnalImage && !$captureOnly;
|
||||||
|
|
||||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists( 'imagecreatefromjpeg' ) )
|
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists( 'imagecreatefromjpeg' ) )
|
||||||
|
@ -1207,22 +1215,20 @@ function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $ov
|
||||||
$thumbPath = $thumbCaptPath;
|
$thumbPath = $thumbCaptPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageFile = ZM_DIR_EVENTS."/".$imagePath;
|
|
||||||
//$thumbFile = ZM_DIR_EVENTS."/".$thumbPath;
|
|
||||||
$thumbFile = $thumbPath;
|
$thumbFile = $thumbPath;
|
||||||
if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) )
|
if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) )
|
||||||
{
|
{
|
||||||
// Get new dimensions
|
// Get new dimensions
|
||||||
list( $imageWidth, $imageHeight ) = getimagesize( $imageFile );
|
list( $imageWidth, $imageHeight ) = getimagesize( $imagePath );
|
||||||
$thumbWidth = $imageWidth * $fraction;
|
$thumbWidth = $imageWidth * $fraction;
|
||||||
$thumbHeight = $imageHeight * $fraction;
|
$thumbHeight = $imageHeight * $fraction;
|
||||||
|
|
||||||
// Resample
|
// Resample
|
||||||
$thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
|
$thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
|
||||||
$image = imagecreatefromjpeg( $imageFile );
|
$image = imagecreatefromjpeg( $imagePath );
|
||||||
imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
|
imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
|
||||||
|
|
||||||
if ( !imagejpeg( $thumbImage, $thumbFile ) )
|
if ( !imagejpeg( $thumbImage, $thumbPath ) )
|
||||||
Error( "Can't create thumbnail '$thumbPath'" );
|
Error( "Can't create thumbnail '$thumbPath'" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1231,15 +1237,13 @@ function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $ov
|
||||||
'eventPath' => $eventPath,
|
'eventPath' => $eventPath,
|
||||||
'imagePath' => $imagePath,
|
'imagePath' => $imagePath,
|
||||||
'thumbPath' => $thumbPath,
|
'thumbPath' => $thumbPath,
|
||||||
'imageFile' => $imageFile,
|
'imageFile' => $imagePath,
|
||||||
'thumbFile' => $thumbFile,
|
'thumbFile' => $thumbFile,
|
||||||
'imageClass' => $alarmFrame?"alarm":"normal",
|
'imageClass' => $alarmFrame?"alarm":"normal",
|
||||||
'isAnalImage' => $isAnalImage,
|
'isAnalImage' => $isAnalImage,
|
||||||
'hasAnalImage' => $hasAnalImage,
|
'hasAnalImage' => $hasAnalImage,
|
||||||
);
|
);
|
||||||
|
|
||||||
//echo "IP:$imagePath<br>";
|
|
||||||
//echo "TP:$thumbPath<br>";
|
|
||||||
return( $imageData );
|
return( $imageData );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -584,6 +584,7 @@ $SLANG = array(
|
||||||
'Protocol' => 'Protocol',
|
'Protocol' => 'Protocol',
|
||||||
'Rate' => 'Rate',
|
'Rate' => 'Rate',
|
||||||
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP
|
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP
|
||||||
|
'RecordAudio' => 'Whether to store the audio stream when saving an event.',
|
||||||
'Real' => 'Real',
|
'Real' => 'Real',
|
||||||
'Record' => 'Record',
|
'Record' => 'Record',
|
||||||
'RefImageBlendPct' => 'Reference Image Blend %ge',
|
'RefImageBlendPct' => 'Reference Image Blend %ge',
|
||||||
|
|
|
@ -55,7 +55,7 @@ $replayModes = array(
|
||||||
if ( isset( $_REQUEST['streamMode'] ) )
|
if ( isset( $_REQUEST['streamMode'] ) )
|
||||||
$streamMode = validHtmlStr($_REQUEST['streamMode']);
|
$streamMode = validHtmlStr($_REQUEST['streamMode']);
|
||||||
else
|
else
|
||||||
$streamMode = video;
|
$streamMode = 'video';
|
||||||
|
|
||||||
if ( isset( $_REQUEST['replayMode'] ) )
|
if ( isset( $_REQUEST['replayMode'] ) )
|
||||||
$replayMode = validHtmlStr($_REQUEST['replayMode']);
|
$replayMode = validHtmlStr($_REQUEST['replayMode']);
|
||||||
|
|
|
@ -86,6 +86,7 @@ if ( ! empty($_REQUEST['mid']) ) {
|
||||||
'SaveJPEGs' => "3",
|
'SaveJPEGs' => "3",
|
||||||
'VideoWriter' => "0",
|
'VideoWriter' => "0",
|
||||||
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||||
|
'RecordAudio' => "0",
|
||||||
'LabelFormat' => '%N - %d/%m/%y %H:%M:%S',
|
'LabelFormat' => '%N - %d/%m/%y %H:%M:%S',
|
||||||
'LabelX' => 0,
|
'LabelX' => 0,
|
||||||
'LabelY' => 0,
|
'LabelY' => 0,
|
||||||
|
@ -593,6 +594,7 @@ if ( $tab != 'storage' )
|
||||||
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($newMonitor['SaveJPEGs']) ?>"/>
|
<input type="hidden" name="newMonitor[SaveJPEGs]" value="<?php echo validHtmlStr($newMonitor['SaveJPEGs']) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($newMonitor['VideoWriter']) ?>"/>
|
<input type="hidden" name="newMonitor[VideoWriter]" value="<?php echo validHtmlStr($newMonitor['VideoWriter']) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($newMonitor['EncoderParameters']) ?>"/>
|
<input type="hidden" name="newMonitor[EncoderParameters]" value="<?php echo validHtmlStr($newMonitor['EncoderParameters']) ?>"/>
|
||||||
|
<input type="hidden" name="newMonitor[RecordAudio]" value="<?php echo validHtmlStr($newMonitor['RecordAudio']) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Protocol'] != 'RTSP'))
|
if ( $tab != 'source' || ($newMonitor['Type'] != 'Remote' && $newMonitor['Protocol'] != 'RTSP'))
|
||||||
|
@ -891,6 +893,7 @@ switch ( $tab )
|
||||||
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['SaveJPEGs'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['SaveJPEGs'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['VideoWriter'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
<tr><td><?php echo translate('VideoWriter') ?></td><td><select name="newMonitor[VideoWriter]"><?php foreach ( $videowriteropts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $newMonitor['VideoWriter'] ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td></tr>
|
||||||
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($newMonitor['EncoderParameters']) ?></textarea></td></tr>
|
<tr><td><?php echo translate('OptionalEncoderParam') ?></td><td><textarea name="newMonitor[EncoderParameters]" rows="4" cols="36"><?php echo validHtmlStr($newMonitor['EncoderParameters']) ?></textarea></td></tr>
|
||||||
|
<tr><td><?php echo translate('RecordAudio') ?></td><td><input type="checkbox" name="newMonitor[RecordAudio]" value="1"<?php if ( !empty($newMonitor['RecordAudio']) ) { ?> checked="checked"<?php } ?>/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
case 'timestamp' :
|
case 'timestamp' :
|
||||||
|
|
Loading…
Reference in New Issue