Merge pull request #1384 from ZoneMinder/tabs_to_spaces

tabs to spaces and use 2space indenting
This commit is contained in:
Steve Gilvarry 2016-04-29 19:27:12 +10:00
commit 66a3fef61e
85 changed files with 34691 additions and 34439 deletions

View File

@ -26,8 +26,8 @@
#include "zm_config.h" #include "zm_config.h"
#ifdef SOLARIS #ifdef SOLARIS
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE #undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
#include <string.h> // define strerror() and friends #include <string.h> // define strerror() and friends
#endif #endif
#include "zm_logger.h" #include "zm_logger.h"

File diff suppressed because it is too large Load Diff

View File

@ -36,39 +36,39 @@
class Box class Box
{ {
private: private:
Coord lo, hi; Coord lo, hi;
Coord size; Coord size;
public: public:
inline Box() inline Box()
{ {
} }
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
inline const Coord &Lo() const { return( lo ); } inline const Coord &Lo() const { return( lo ); }
inline int LoX() const { return( lo.X() ); } inline int LoX() const { return( lo.X() ); }
inline int LoY() const { return( lo.Y() ); } inline int LoY() const { return( lo.Y() ); }
inline const Coord &Hi() const { return( hi ); } inline const Coord &Hi() const { return( hi ); }
inline int HiX() const { return( hi.X() ); } inline int HiX() const { return( hi.X() ); }
inline int HiY() const { return( hi.Y() ); } inline int HiY() const { return( hi.Y() ); }
inline const Coord &Size() const { return( size ); } inline const Coord &Size() const { return( size ); }
inline int Width() const { return( size.X() ); } inline int Width() const { return( size.X() ); }
inline int Height() const { return( size.Y() ); } inline int Height() const { return( size.Y() ); }
inline int Area() const { return( size.X()*size.Y() ); } inline int Area() const { return( size.X()*size.Y() ); }
inline const Coord Centre() const inline const Coord Centre() const
{ {
int mid_x = int(round(lo.X()+(size.X()/2.0))); int mid_x = int(round(lo.X()+(size.X()/2.0)));
int mid_y = int(round(lo.Y()+(size.Y()/2.0))); int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
return( Coord( mid_x, mid_y ) ); return( Coord( mid_x, mid_y ) );
} }
inline bool Inside( const Coord &coord ) const inline bool Inside( const Coord &coord ) const
{ {
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
} }
}; };
#endif // ZM_BOX_H #endif // ZM_BOX_H

View File

@ -25,57 +25,57 @@
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize ) unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
{ {
if ( mAllocation < pSize ) if ( mAllocation < pSize )
{ {
delete[] mStorage; delete[] mStorage;
mAllocation = pSize; mAllocation = pSize;
mHead = mStorage = new unsigned char[pSize]; mHead = mStorage = new unsigned char[pSize];
} }
mSize = pSize; mSize = pSize;
memcpy( mStorage, pStorage, mSize ); memcpy( mStorage, pStorage, mSize );
mHead = mStorage; mHead = mStorage;
mTail = mHead + mSize; mTail = mHead + mSize;
return( mSize ); return( mSize );
} }
unsigned int Buffer::expand( unsigned int count ) unsigned int Buffer::expand( unsigned int count )
{ {
int spare = mAllocation - mSize; int spare = mAllocation - mSize;
int headSpace = mHead - mStorage; int headSpace = mHead - mStorage;
int tailSpace = spare - headSpace; int tailSpace = spare - headSpace;
int width = mTail - mHead; int width = mTail - mHead;
if ( spare > (int)count ) if ( spare > (int)count )
{
if ( tailSpace < (int)count )
{ {
if ( tailSpace < (int)count ) memmove( mStorage, mHead, mSize );
{ mHead = mStorage;
memmove( mStorage, mHead, mSize ); mTail = mHead + width;
mHead = mStorage;
mTail = mHead + width;
}
} }
else }
else
{
mAllocation += count;
unsigned char *newStorage = new unsigned char[mAllocation];
if ( mStorage )
{ {
mAllocation += count; memcpy( newStorage, mHead, mSize );
unsigned char *newStorage = new unsigned char[mAllocation]; delete[] mStorage;
if ( mStorage )
{
memcpy( newStorage, mHead, mSize );
delete[] mStorage;
}
mStorage = newStorage;
mHead = mStorage;
mTail = mHead + width;
} }
return( mSize ); mStorage = newStorage;
mHead = mStorage;
mTail = mHead + width;
}
return( mSize );
} }
int Buffer::read_into( int sd, unsigned int bytes ) { int Buffer::read_into( int sd, unsigned int bytes ) {
// Make sure there is enough space // Make sure there is enough space
this->expand(bytes); this->expand(bytes);
int bytes_read = read( sd, mTail, bytes ); int bytes_read = read( sd, mTail, bytes );
if ( bytes_read > 0 ) { if ( bytes_read > 0 ) {
mTail += bytes_read; mTail += bytes_read;
mSize += bytes_read; mSize += bytes_read;
} }
return bytes_read; return bytes_read;
} }

View File

@ -27,183 +27,183 @@
class Buffer class Buffer
{ {
protected: protected:
unsigned char *mStorage; unsigned char *mStorage;
unsigned int mAllocation; unsigned int mAllocation;
unsigned int mSize; unsigned int mSize;
unsigned char *mHead; unsigned char *mHead;
unsigned char *mTail; unsigned char *mTail;
public: public:
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 ) Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
{
}
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
{
mHead = mStorage = new unsigned char[mAllocation];
mTail = mHead;
}
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
{
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, pStorage, mSize );
mTail = mHead + mSize;
}
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
{
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, buffer.mHead, mSize );
mTail = mHead + mSize;
}
~Buffer()
{
delete[] mStorage;
}
unsigned char *head() const { return( mHead ); }
unsigned char *tail() const { return( mTail ); }
unsigned int size() const { return( mSize ); }
bool empty() const { return( mSize == 0 ); }
unsigned int size( unsigned int pSize )
{
if ( mSize < pSize )
{ {
expand( pSize-mSize );
} }
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 ) return( mSize );
{ }
mHead = mStorage = new unsigned char[mAllocation]; //unsigned int Allocation() const { return( mAllocation ); }
mTail = mHead;
}
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
{
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, pStorage, mSize );
mTail = mHead + mSize;
}
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
{
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, buffer.mHead, mSize );
mTail = mHead + mSize;
}
~Buffer()
{
delete[] mStorage;
}
unsigned char *head() const { return( mHead ); }
unsigned char *tail() const { return( mTail ); }
unsigned int size() const { return( mSize ); }
bool empty() const { return( mSize == 0 ); }
unsigned int size( unsigned int pSize )
{
if ( mSize < pSize )
{
expand( pSize-mSize );
}
return( mSize );
}
//unsigned int Allocation() const { return( mAllocation ); }
void clear() void clear()
{
mSize = 0;
mHead = mTail = mStorage;
}
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
unsigned int assign( const Buffer &buffer )
{
return( assign( buffer.mHead, buffer.mSize ) );
}
// Trim from the front of the buffer
unsigned int consume( unsigned int count )
{
if ( count > mSize )
{ {
mSize = 0; Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
count = mSize;
}
mHead += count;
mSize -= count;
tidy( 0 );
return( count );
}
// Trim from the end of the buffer
unsigned int shrink( unsigned int count )
{
if ( count > mSize )
{
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
count = mSize;
}
mSize -= count;
if ( mTail > (mHead + mSize) )
mTail = mHead + mSize;
tidy( 0 );
return( count );
}
// Add to the end of the buffer
unsigned int expand( unsigned int count );
// Return pointer to the first pSize bytes and advance the head
unsigned char *extract( unsigned int pSize )
{
if ( pSize > mSize )
{
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
pSize = mSize;
}
unsigned char *oldHead = mHead;
mHead += pSize;
mSize -= pSize;
tidy( 0 );
return( oldHead );
}
// Add bytes to the end of the buffer
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
{
expand( pSize );
memcpy( mTail, pStorage, pSize );
mTail += pSize;
mSize += pSize;
return( mSize );
}
unsigned int append( const char *pStorage, unsigned int pSize )
{
return( append( (const unsigned char *)pStorage, pSize ) );
}
unsigned int append( const Buffer &buffer )
{
return( append( buffer.mHead, buffer.mSize ) );
}
void tidy( bool level=0 )
{
if ( mHead != mStorage )
{
if ( mSize == 0 )
mHead = mTail = mStorage; mHead = mTail = mStorage;
} else if ( level )
{
unsigned int assign( const unsigned char *pStorage, unsigned int pSize ); if ( (mHead-mStorage) > mSize )
unsigned int assign( const Buffer &buffer )
{
return( assign( buffer.mHead, buffer.mSize ) );
}
// Trim from the front of the buffer
unsigned int consume( unsigned int count )
{
if ( count > mSize )
{ {
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize ); memcpy( mStorage, mHead, mSize );
count = mSize; mHead = mStorage;
mTail = mHead + mSize;
} }
mHead += count; }
mSize -= count;
tidy( 0 );
return( count );
} }
// Trim from the end of the buffer }
unsigned int shrink( unsigned int count )
{
if ( count > mSize )
{
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
count = mSize;
}
mSize -= count;
if ( mTail > (mHead + mSize) )
mTail = mHead + mSize;
tidy( 0 );
return( count );
}
// Add to the end of the buffer
unsigned int expand( unsigned int count );
// Return pointer to the first pSize bytes and advance the head Buffer &operator=( const Buffer &buffer )
unsigned char *extract( unsigned int pSize ) {
{ assign( buffer );
if ( pSize > mSize ) return( *this );
{ }
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize ); Buffer &operator+=( const Buffer &buffer )
pSize = mSize; {
} append( buffer );
unsigned char *oldHead = mHead; return( *this );
mHead += pSize; }
mSize -= pSize; Buffer &operator+=( unsigned int count )
tidy( 0 ); {
return( oldHead ); expand( count );
} return( *this );
// Add bytes to the end of the buffer }
unsigned int append( const unsigned char *pStorage, unsigned int pSize ) Buffer &operator-=( unsigned int count )
{ {
expand( pSize ); consume( count );
memcpy( mTail, pStorage, pSize ); return( *this );
mTail += pSize; }
mSize += pSize; operator unsigned char *() const
return( mSize ); {
} return( mHead );
unsigned int append( const char *pStorage, unsigned int pSize ) }
{ operator char *() const
return( append( (const unsigned char *)pStorage, pSize ) ); {
} return( (char *)mHead );
unsigned int append( const Buffer &buffer ) }
{ unsigned char *operator+(int offset) const
return( append( buffer.mHead, buffer.mSize ) ); {
} return( (unsigned char *)(mHead+offset) );
void tidy( bool level=0 ) }
{ unsigned char operator[](int index) const
if ( mHead != mStorage ) {
{ return( *(mHead+index) );
if ( mSize == 0 ) }
mHead = mTail = mStorage; operator int () const
else if ( level ) {
{ return( (int)mSize );
if ( (mHead-mStorage) > mSize ) }
{ int read_into( int sd, unsigned int bytes );
memcpy( mStorage, mHead, mSize );
mHead = mStorage;
mTail = mHead + mSize;
}
}
}
}
Buffer &operator=( const Buffer &buffer )
{
assign( buffer );
return( *this );
}
Buffer &operator+=( const Buffer &buffer )
{
append( buffer );
return( *this );
}
Buffer &operator+=( unsigned int count )
{
expand( count );
return( *this );
}
Buffer &operator-=( unsigned int count )
{
consume( count );
return( *this );
}
operator unsigned char *() const
{
return( mHead );
}
operator char *() const
{
return( (char *)mHead );
}
unsigned char *operator+(int offset) const
{
return( (unsigned char *)(mHead+offset) );
}
unsigned char operator[](int index) const
{
return( *(mHead+index) );
}
operator int () const
{
return( (int)mSize );
}
int read_into( int sd, unsigned int bytes );
}; };
#endif // ZM_BUFFER_H #endif // ZM_BUFFER_H

View File

@ -21,29 +21,29 @@
#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 ) :
id( p_id ), id( p_id ),
type( p_type ), type( p_type ),
width( p_width), width( p_width),
height( p_height ), height( p_height ),
colours( p_colours ), colours( p_colours ),
subpixelorder( p_subpixelorder ), subpixelorder( p_subpixelorder ),
brightness( p_brightness ), brightness( p_brightness ),
hue( p_hue ), hue( p_hue ),
colour( p_colour ), colour( p_colour ),
contrast( p_contrast ), contrast( p_contrast ),
capture( p_capture ) capture( p_capture )
{ {
pixels = width * height; pixels = width * height;
imagesize = pixels * colours; imagesize = pixels * colours;
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
Fatal("Image size is not multiples of 16"); Fatal("Image size is not multiples of 16");
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
Fatal("Image size is not multiples of 12 and 16"); Fatal("Image size is not multiples of 12 and 16");
} }
} }
Camera::~Camera() Camera::~Camera()

View File

@ -32,52 +32,52 @@
class Camera class Camera
{ {
protected: protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
int id; int id;
SourceType type; SourceType type;
unsigned int width; unsigned int width;
unsigned int height; unsigned int height;
unsigned int colours; unsigned int colours;
unsigned int subpixelorder; unsigned int subpixelorder;
unsigned int pixels; unsigned int pixels;
unsigned int imagesize; unsigned int imagesize;
int brightness; int brightness;
int hue; int hue;
int colour; int colour;
int contrast; int contrast;
bool capture; bool capture;
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 );
virtual ~Camera(); virtual ~Camera();
int getId() const { return( id ); } int getId() const { return( id ); }
SourceType Type() const { return( type ); } SourceType Type() const { return( type ); }
bool IsLocal() const { return( type == LOCAL_SRC ); } bool IsLocal() const { return( type == LOCAL_SRC ); }
bool IsRemote() const { return( type == REMOTE_SRC ); } bool IsRemote() const { return( type == REMOTE_SRC ); }
bool IsFile() const { return( type == FILE_SRC ); } bool IsFile() const { return( type == FILE_SRC ); }
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
bool IsLibvlc() const { return( type == LIBVLC_SRC ); } bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
bool IscURL() const { return( type == CURL_SRC ); } bool IscURL() const { return( type == CURL_SRC ); }
unsigned int Width() const { return( width ); } unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); } unsigned int Height() const { return( height ); }
unsigned int Colours() const { return( colours ); } unsigned int Colours() const { return( colours ); }
unsigned int SubpixelOrder() const { return( subpixelorder ); } unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); } unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); } unsigned int ImageSize() const { return( imagesize ); }
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
bool CanCapture() const { return( capture ); } bool CanCapture() const { return( capture ); }
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;
}; };
#endif // ZM_CAMERA_H #endif // ZM_CAMERA_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,277 +29,277 @@
void zmLoadConfig() void zmLoadConfig()
{ {
FILE *cfg; FILE *cfg;
char line[512]; char line[512];
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
{ {
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
} }
while ( fgets( line, sizeof(line), cfg ) != NULL ) while ( fgets( line, sizeof(line), cfg ) != NULL )
{ {
char *line_ptr = line; char *line_ptr = line;
// Trim off any cr/lf line endings // Trim off any cr/lf line endings
int chomp_len = strcspn( line_ptr, "\r\n" ); int chomp_len = strcspn( line_ptr, "\r\n" );
line_ptr[chomp_len] = '\0'; line_ptr[chomp_len] = '\0';
// Remove leading white space // Remove leading white space
int white_len = strspn( line_ptr, " \t" ); int white_len = strspn( line_ptr, " \t" );
line_ptr += white_len; line_ptr += white_len;
// Check for comment or empty line // Check for comment or empty line
if ( *line_ptr == '\0' || *line_ptr == '#' ) if ( *line_ptr == '\0' || *line_ptr == '#' )
continue; continue;
// Remove trailing white space // Remove trailing white space
char *temp_ptr = line_ptr+strlen(line_ptr)-1; char *temp_ptr = line_ptr+strlen(line_ptr)-1;
while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
{ {
*temp_ptr-- = '\0'; *temp_ptr-- = '\0';
temp_ptr--; temp_ptr--;
} }
// Now look for the '=' in the middle of the line // Now look for the '=' in the middle of the line
temp_ptr = strchr( line_ptr, '=' ); temp_ptr = strchr( line_ptr, '=' );
if ( !temp_ptr ) if ( !temp_ptr )
{ {
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
continue; continue;
} }
// Assign the name and value parts // Assign the name and value parts
char *name_ptr = line_ptr; char *name_ptr = line_ptr;
char *val_ptr = temp_ptr+1; char *val_ptr = temp_ptr+1;
// Trim trailing space from the name part // Trim trailing space from the name part
do do
{ {
*temp_ptr = '\0'; *temp_ptr = '\0';
temp_ptr--; temp_ptr--;
} }
while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
// Remove leading white space from the value part // Remove leading white space from the value part
white_len = strspn( val_ptr, " \t" ); white_len = strspn( val_ptr, " \t" );
val_ptr += white_len; val_ptr += white_len;
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
staticConfig.DB_HOST = std::string(val_ptr); staticConfig.DB_HOST = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
staticConfig.DB_NAME = std::string(val_ptr); staticConfig.DB_NAME = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
staticConfig.DB_USER = std::string(val_ptr); staticConfig.DB_USER = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
staticConfig.DB_PASS = std::string(val_ptr); staticConfig.DB_PASS = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
staticConfig.PATH_WEB = std::string(val_ptr); staticConfig.PATH_WEB = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
staticConfig.SERVER_NAME = std::string(val_ptr); staticConfig.SERVER_NAME = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
staticConfig.SERVER_NAME = std::string(val_ptr); staticConfig.SERVER_NAME = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 ) else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
staticConfig.SERVER_ID = atoi(val_ptr); staticConfig.SERVER_ID = atoi(val_ptr);
else else
{ {
// We ignore this now as there may be more parameters than the // We ignore this now as there may be more parameters than the
// c/c++ binaries are bothered about // c/c++ binaries are bothered about
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
} }
} // end foreach line of the config } // end foreach line of the config
fclose( cfg ); fclose( cfg );
zmDbConnect(); zmDbConnect();
config.Load(); config.Load();
config.Assign(); config.Assign();
// Populate the server config entries // Populate the server config entries
if ( ! staticConfig.SERVER_ID ) { if ( ! staticConfig.SERVER_ID ) {
if ( ! staticConfig.SERVER_NAME.empty() ) { if ( ! staticConfig.SERVER_NAME.empty() ) {
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
staticConfig.SERVER_ID = atoi(dbrow[0]); staticConfig.SERVER_ID = atoi(dbrow[0]);
} else { } else {
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
} }
} // end if has SERVER_NAME } // end if has SERVER_NAME
} else if ( staticConfig.SERVER_NAME.empty() ) { } else if ( staticConfig.SERVER_NAME.empty() ) {
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
staticConfig.SERVER_NAME = std::string(dbrow[0]); staticConfig.SERVER_NAME = std::string(dbrow[0]);
} else { } else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
} }
} }
if ( ! staticConfig.SERVER_ID ) { if ( ! staticConfig.SERVER_ID ) {
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." ); Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." );
} else { } else {
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID ); Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
} }
} }
StaticConfig staticConfig; StaticConfig staticConfig;
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type ) ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type )
{ {
name = new char[strlen(p_name)+1]; name = new char[strlen(p_name)+1];
strcpy( name, p_name ); strcpy( name, p_name );
value = new char[strlen(p_value)+1]; value = new char[strlen(p_value)+1];
strcpy( value, p_value ); strcpy( value, p_value );
type = new char[strlen(p_type)+1]; type = new char[strlen(p_type)+1];
strcpy( type, p_type ); strcpy( type, p_type );
//Info( "Created new config item %s = %s (%s)\n", name, value, type ); //Info( "Created new config item %s = %s (%s)\n", name, value, type );
accessed = false; accessed = false;
} }
ConfigItem::~ConfigItem() ConfigItem::~ConfigItem()
{ {
delete[] name; delete[] name;
delete[] value; delete[] value;
delete[] type; delete[] type;
} }
void ConfigItem::ConvertValue() const void ConfigItem::ConvertValue() const
{ {
if ( !strcmp( type, "boolean" ) ) if ( !strcmp( type, "boolean" ) )
{ {
cfg_type = CFG_BOOLEAN; cfg_type = CFG_BOOLEAN;
cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
} }
else if ( !strcmp( type, "integer" ) ) else if ( !strcmp( type, "integer" ) )
{ {
cfg_type = CFG_INTEGER; cfg_type = CFG_INTEGER;
cfg_value.integer_value = strtol( value, 0, 10 ); cfg_value.integer_value = strtol( value, 0, 10 );
} }
else if ( !strcmp( type, "hexadecimal" ) ) else if ( !strcmp( type, "hexadecimal" ) )
{ {
cfg_type = CFG_INTEGER; cfg_type = CFG_INTEGER;
cfg_value.integer_value = strtol( value, 0, 16 ); cfg_value.integer_value = strtol( value, 0, 16 );
} }
else if ( !strcmp( type, "decimal" ) ) else if ( !strcmp( type, "decimal" ) )
{ {
cfg_type = CFG_DECIMAL; cfg_type = CFG_DECIMAL;
cfg_value.decimal_value = strtod( value, 0 ); cfg_value.decimal_value = strtod( value, 0 );
} }
else else
{ {
cfg_type = CFG_STRING; cfg_type = CFG_STRING;
cfg_value.string_value = value; cfg_value.string_value = value;
} }
accessed = true; accessed = true;
} }
bool ConfigItem::BooleanValue() const bool ConfigItem::BooleanValue() const
{ {
if ( !accessed ) if ( !accessed )
ConvertValue(); ConvertValue();
if ( cfg_type != CFG_BOOLEAN ) if ( cfg_type != CFG_BOOLEAN )
{ {
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 ); exit( -1 );
} }
return( cfg_value.boolean_value ); return( cfg_value.boolean_value );
} }
int ConfigItem::IntegerValue() const int ConfigItem::IntegerValue() const
{ {
if ( !accessed ) if ( !accessed )
ConvertValue(); ConvertValue();
if ( cfg_type != CFG_INTEGER ) if ( cfg_type != CFG_INTEGER )
{ {
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 ); exit( -1 );
} }
return( cfg_value.integer_value ); return( cfg_value.integer_value );
} }
double ConfigItem::DecimalValue() const double ConfigItem::DecimalValue() const
{ {
if ( !accessed ) if ( !accessed )
ConvertValue(); ConvertValue();
if ( cfg_type != CFG_DECIMAL ) if ( cfg_type != CFG_DECIMAL )
{ {
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 ); exit( -1 );
} }
return( cfg_value.decimal_value ); return( cfg_value.decimal_value );
} }
const char *ConfigItem::StringValue() const const char *ConfigItem::StringValue() const
{ {
if ( !accessed ) if ( !accessed )
ConvertValue(); ConvertValue();
if ( cfg_type != CFG_STRING ) if ( cfg_type != CFG_STRING )
{ {
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 ); exit( -1 );
} }
return( cfg_value.string_value ); return( cfg_value.string_value );
} }
Config::Config() Config::Config()
{ {
n_items = 0; n_items = 0;
items = 0; items = 0;
} }
Config::~Config() Config::~Config()
{ {
if ( items ) if ( items )
{ {
for ( int i = 0; i < n_items; i++ ) for ( int i = 0; i < n_items; i++ )
{ {
delete items[i]; delete items[i];
} }
delete[] items; delete[] items;
} }
} }
void Config::Load() void Config::Load()
{ {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
if ( mysql_query( &dbconn, sql ) ) if ( mysql_query( &dbconn, sql ) )
{ {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result ) if ( !result )
{ {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
n_items = mysql_num_rows( result ); n_items = mysql_num_rows( result );
if ( n_items <= ZM_MAX_CFG_ID ) if ( n_items <= ZM_MAX_CFG_ID )
{ {
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
exit( -1 ); exit( -1 );
} }
items = new ConfigItem *[n_items]; items = new ConfigItem *[n_items];
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
{ {
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
} }
mysql_free_result( result ); mysql_free_result( result );
} }
void Config::Assign() void Config::Assign()
@ -309,27 +309,27 @@ ZM_CFG_ASSIGN_LIST
const ConfigItem &Config::Item( int id ) const ConfigItem &Config::Item( int id )
{ {
if ( !n_items ) if ( !n_items )
{ {
Load(); Load();
Assign(); Assign();
} }
if ( id < 0 || id > ZM_MAX_CFG_ID ) if ( id < 0 || id > ZM_MAX_CFG_ID )
{ {
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
exit( -1 ); exit( -1 );
} }
ConfigItem *item = items[id]; ConfigItem *item = items[id];
if ( !item ) if ( !item )
{ {
Error( "Can't find config item %d", id ); Error( "Can't find config item %d", id );
exit( -1 ); exit( -1 );
} }
return( *item ); return( *item );
} }
Config config; Config config;

View File

@ -25,48 +25,48 @@
#include <string> #include <string>
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file #define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
#define ZM_VERSION "@VERSION@" // ZoneMinder Version #define ZM_VERSION "@VERSION@" // ZoneMinder Version
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@ #define ZM_HAS_V4L1 @ZM_HAS_V4L1@
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@ #define ZM_HAS_V4L2 @ZM_HAS_V4L2@
#define ZM_HAS_V4L @ZM_HAS_V4L@ #define ZM_HAS_V4L @ZM_HAS_V4L@
#ifdef HAVE_LIBAVFORMAT #ifdef HAVE_LIBAVFORMAT
#define ZM_HAS_FFMPEG 1 #define ZM_HAS_FFMPEG 1
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT) #define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS) #define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP #define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP #define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements #define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer #define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer #define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer #define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer #define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle #define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc #define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc #define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
extern void zmLoadConfig(); extern void zmLoadConfig();
struct StaticConfig struct StaticConfig
{ {
std::string DB_HOST; std::string DB_HOST;
std::string DB_NAME; std::string DB_NAME;
std::string DB_USER; std::string DB_USER;
std::string DB_PASS; std::string DB_PASS;
std::string PATH_WEB; std::string PATH_WEB;
std::string SERVER_NAME; std::string SERVER_NAME;
unsigned int SERVER_ID; unsigned int SERVER_ID;
}; };
extern StaticConfig staticConfig; extern StaticConfig staticConfig;
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
class ConfigItem class ConfigItem
{ {
private: private:
char *name; char *name;
char *value; char *value;
char *type; char *type;
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type; mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
mutable union mutable union
{ {
bool boolean_value; bool boolean_value;
int integer_value; int integer_value;
double decimal_value; double decimal_value;
char *string_value; char *string_value;
} cfg_value; } cfg_value;
mutable bool accessed; mutable bool accessed;
public: public:
ConfigItem( const char *p_name, const char *p_value, const char *const p_type ); ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
~ConfigItem(); ~ConfigItem();
void ConvertValue() const; void ConvertValue() const;
bool BooleanValue() const; bool BooleanValue() const;
int IntegerValue() const; int IntegerValue() const;
double DecimalValue() const; double DecimalValue() const;
const char *StringValue() const; const char *StringValue() const;
inline operator bool() const inline operator bool() const
{ {
return( BooleanValue() ); return( BooleanValue() );
} }
inline operator int() const inline operator int() const
{ {
return( IntegerValue() ); return( IntegerValue() );
} }
inline operator double() const inline operator double() const
{ {
return( DecimalValue() ); return( DecimalValue() );
} }
inline operator const char *() const inline operator const char *() const
{ {
return( StringValue() ); return( StringValue() );
} }
}; };
class Config class Config
{ {
public: public:
ZM_CFG_DECLARE_LIST ZM_CFG_DECLARE_LIST
private: private:
int n_items; int n_items;
ConfigItem **items; ConfigItem **items;
public: public:
Config(); Config();
~Config(); ~Config();
void Load(); void Load();
void Assign(); void Assign();
const ConfigItem &Item( int id ); const ConfigItem &Item( int id );
}; };
extern Config config; extern Config config;

View File

@ -28,40 +28,40 @@
class Coord class Coord
{ {
private: private:
int x, y; int x, y;
public: public:
inline Coord() : x(0), y(0) inline Coord() : x(0), y(0)
{ {
} }
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y) inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
{ {
} }
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y) inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
{ {
} }
inline int &X() { return( x ); } inline int &X() { return( x ); }
inline const int &X() const { return( x ); } inline const int &X() const { return( x ); }
inline int &Y() { return( y ); } inline int &Y() { return( y ); }
inline const int &Y() const { return( y ); } inline const int &Y() const { return( y ); }
inline static Coord Range( const Coord &coord1, const Coord &coord2 ) inline static Coord Range( const Coord &coord1, const Coord &coord2 )
{ {
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
return( result ); return( result );
} }
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); } inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); } inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); } inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); } inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); } inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); } inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); } inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); } inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
}; };
#endif // ZM_COORD_H #endif // ZM_COORD_H

View File

@ -31,523 +31,523 @@ 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 ) :
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 ),
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 )
{ {
if ( capture ) if ( capture )
{ {
Initialise(); Initialise();
} }
} }
cURLCamera::~cURLCamera() cURLCamera::~cURLCamera()
{ {
if ( capture ) if ( capture )
{ {
Terminate(); Terminate();
} }
} }
void cURLCamera::Initialise() void cURLCamera::Initialise()
{ {
content_length_match_len = strlen(content_length_match); content_length_match_len = strlen(content_length_match);
content_type_match_len = strlen(content_type_match); content_type_match_len = strlen(content_type_match);
databuffer.expand(CURL_BUFFER_INITIAL_SIZE); databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
/* cURL initialization */ /* cURL initialization */
cRet = curl_global_init(CURL_GLOBAL_ALL); cRet = curl_global_init(CURL_GLOBAL_ALL);
if(cRet != CURLE_OK) { if(cRet != CURLE_OK) {
Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet));
} }
Debug(2,"libcurl version: %s",curl_version()); Debug(2,"libcurl version: %s",curl_version());
/* Create the shared data mutex */ /* Create the shared data mutex */
nRet = pthread_mutex_init(&shareddata_mutex, NULL); nRet = pthread_mutex_init(&shareddata_mutex, NULL);
if(nRet != 0) { if(nRet != 0) {
Fatal("Shared data mutex creation failed: %s",strerror(nRet)); Fatal("Shared data mutex creation failed: %s",strerror(nRet));
} }
/* Create the data available condition variable */ /* Create the data available condition variable */
nRet = pthread_cond_init(&data_available_cond, NULL); nRet = pthread_cond_init(&data_available_cond, NULL);
if(nRet != 0) { if(nRet != 0) {
Fatal("Data available condition variable creation failed: %s",strerror(nRet)); Fatal("Data available condition variable creation failed: %s",strerror(nRet));
} }
/* Create the request complete condition variable */ /* Create the request complete condition variable */
nRet = pthread_cond_init(&request_complete_cond, NULL); nRet = pthread_cond_init(&request_complete_cond, NULL);
if(nRet != 0) { if(nRet != 0) {
Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); Fatal("Request complete condition variable creation failed: %s",strerror(nRet));
} }
/* Create the thread */ /* Create the thread */
nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this);
if(nRet != 0) { if(nRet != 0) {
Fatal("Thread creation failed: %s",strerror(nRet)); Fatal("Thread creation failed: %s",strerror(nRet));
} }
} }
void cURLCamera::Terminate() void cURLCamera::Terminate()
{ {
/* Signal the thread to terminate */ /* Signal the thread to terminate */
bTerminate = true; bTerminate = true;
/* Wait for thread termination */ /* Wait for thread termination */
pthread_join(thread, NULL); pthread_join(thread, NULL);
/* Destroy condition variables */ /* Destroy condition variables */
pthread_cond_destroy(&request_complete_cond); pthread_cond_destroy(&request_complete_cond);
pthread_cond_destroy(&data_available_cond); pthread_cond_destroy(&data_available_cond);
/* Destroy mutex */ /* Destroy mutex */
pthread_mutex_destroy(&shareddata_mutex); pthread_mutex_destroy(&shareddata_mutex);
/* cURL cleanup */ /* cURL cleanup */
curl_global_cleanup(); curl_global_cleanup();
} }
int cURLCamera::PrimeCapture() int cURLCamera::PrimeCapture()
{ {
//Info( "Priming capture from %s", mPath.c_str() ); //Info( "Priming capture from %s", mPath.c_str() );
return 0; return 0;
} }
int cURLCamera::PreCapture() int cURLCamera::PreCapture()
{ {
// Nothing to do here // Nothing to do here
return( 0 ); return( 0 );
} }
int cURLCamera::Capture( Image &image ) int cURLCamera::Capture( Image &image )
{ {
bool frameComplete = false; bool frameComplete = false;
/* MODE_STREAM specific variables */ /* MODE_STREAM specific variables */
bool SubHeadersParsingComplete = false; bool SubHeadersParsingComplete = false;
unsigned int frame_content_length = 0; unsigned int frame_content_length = 0;
std::string frame_content_type; std::string frame_content_type;
bool need_more_data = false; bool need_more_data = false;
/* Grab the mutex to ensure exclusive access to the shared data */ /* Grab the mutex to ensure exclusive access to the shared data */
lock(); lock();
while (!frameComplete) { while (!frameComplete) {
/* If the work thread did a reset, reset our local variables */ /* If the work thread did a reset, reset our local variables */
if(bReset) { if(bReset) {
SubHeadersParsingComplete = false; SubHeadersParsingComplete = false;
frame_content_length = 0; frame_content_length = 0;
frame_content_type.clear(); frame_content_type.clear();
need_more_data = false; need_more_data = false;
bReset = false; bReset = false;
} }
if(mode == MODE_UNSET) { if(mode == MODE_UNSET) {
/* Don't have a mode yet. Sleep while waiting for data */ /* Don't have a mode yet. Sleep while waiting for data */
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
if(nRet != 0) { if(nRet != 0) {
Error("Failed waiting for available data condition variable: %s",strerror(nRet)); Error("Failed waiting for available data condition variable: %s",strerror(nRet));
return -20; return -20;
} }
} }
if(mode == MODE_STREAM) { if(mode == MODE_STREAM) {
/* Subheader parsing */ /* Subheader parsing */
while(!SubHeadersParsingComplete && !need_more_data) { while(!SubHeadersParsingComplete && !need_more_data) {
size_t crlf_start, crlf_end, crlf_size; size_t crlf_start, crlf_end, crlf_size;
std::string subheader; std::string subheader;
/* Check if the buffer contains something */ /* Check if the buffer contains something */
if(databuffer.empty()) { if(databuffer.empty()) {
/* Empty buffer, wait for data */ /* Empty buffer, wait for data */
need_more_data = true; need_more_data = true;
break; break;
} }
/* Find crlf start */ /* Find crlf start */
crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
if(crlf_start == databuffer.size()) { if(crlf_start == databuffer.size()) {
/* Not found, wait for more data */ /* Not found, wait for more data */
need_more_data = true; need_more_data = true;
break; break;
} }
/* See if we have enough data for determining crlf length */ /* See if we have enough data for determining crlf length */
if(databuffer.size() < crlf_start+5) { if(databuffer.size() < crlf_start+5) {
/* Need more data */ /* Need more data */
need_more_data = true; need_more_data = true;
break; break;
} }
/* Find crlf end and calculate crlf size */ /* Find crlf end and calculate crlf size */
crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
crlf_size = (crlf_start + crlf_end) - crlf_start; crlf_size = (crlf_start + crlf_end) - crlf_start;
/* Is this the end of a previous stream? (This is just before the boundary) */ /* Is this the end of a previous stream? (This is just before the boundary) */
if(crlf_start == 0) { if(crlf_start == 0) {
databuffer.consume(crlf_size); databuffer.consume(crlf_size);
continue; continue;
} }
/* Check for invalid CRLF size */ /* Check for invalid CRLF size */
if(crlf_size > 4) { if(crlf_size > 4) {
Error("Invalid CRLF length"); Error("Invalid CRLF length");
} }
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
/* This is the last header */ /* This is the last header */
SubHeadersParsingComplete = true; SubHeadersParsingComplete = true;
} }
/* Copy the subheader, excluding the crlf */ /* Copy the subheader, excluding the crlf */
subheader.assign(databuffer, crlf_start); subheader.assign(databuffer, crlf_start);
/* Advance the buffer past this one */ /* Advance the buffer past this one */
databuffer.consume(crlf_start+crlf_size); databuffer.consume(crlf_start+crlf_size);
Debug(7,"Got subheader: %s",subheader.c_str()); Debug(7,"Got subheader: %s",subheader.c_str());
/* Find where the data in this header starts */ /* Find where the data in this header starts */
size_t subheader_data_start = subheader.rfind(' '); size_t subheader_data_start = subheader.rfind(' ');
if(subheader_data_start == std::string::npos) { if(subheader_data_start == std::string::npos) {
subheader_data_start = subheader.find(':'); subheader_data_start = subheader.find(':');
} }
/* Extract the data into a string */ /* Extract the data into a string */
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
Debug(8,"Got subheader data: %s",subheader_data.c_str()); Debug(8,"Got subheader data: %s",subheader_data.c_str());
/* Check the header */ /* Check the header */
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
/* Found the content-length header */ /* Found the content-length header */
frame_content_length = atoi(subheader_data.c_str()); frame_content_length = atoi(subheader_data.c_str());
Debug(6,"Got content-length subheader: %d",frame_content_length); Debug(6,"Got content-length subheader: %d",frame_content_length);
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
/* Found the content-type header */ /* Found the content-type header */
frame_content_type = subheader_data; frame_content_type = subheader_data;
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
} }
} }
/* Attempt to extract the frame */ /* Attempt to extract the frame */
if(!need_more_data) { if(!need_more_data) {
if(!SubHeadersParsingComplete) { if(!SubHeadersParsingComplete) {
/* We haven't parsed all headers yet */ /* We haven't parsed all headers yet */
need_more_data = true; need_more_data = true;
} else if(frame_content_length <= 0) { } else if(frame_content_length <= 0) {
/* Invalid frame */ /* Invalid frame */
Error("Invalid frame: invalid content length"); Error("Invalid frame: invalid content length");
} else if(frame_content_type != "image/jpeg") { } else if(frame_content_type != "image/jpeg") {
/* Unsupported frame type */ /* Unsupported frame type */
Error("Unsupported frame: %s",frame_content_type.c_str()); Error("Unsupported frame: %s",frame_content_type.c_str());
} else if(frame_content_length > databuffer.size()) { } else if(frame_content_length > databuffer.size()) {
/* Incomplete frame, wait for more data */ /* Incomplete frame, wait for more data */
need_more_data = true; need_more_data = true;
} else { } else {
/* All good. decode the image */ /* All good. decode the image */
image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
frameComplete = true; frameComplete = true;
} }
} }
/* Attempt to get more data */ /* Attempt to get more data */
if(need_more_data) { if(need_more_data) {
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
if(nRet != 0) { if(nRet != 0) {
Error("Failed waiting for available data condition variable: %s",strerror(nRet)); Error("Failed waiting for available data condition variable: %s",strerror(nRet));
return -18; return -18;
} }
need_more_data = false; need_more_data = false;
} }
} else if(mode == MODE_SINGLE) { } else if(mode == MODE_SINGLE) {
/* Check if we have anything */ /* Check if we have anything */
if (!single_offsets.empty()) { if (!single_offsets.empty()) {
if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
/* Extract frame */ /* Extract frame */
image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
single_offsets.pop_front(); single_offsets.pop_front();
frameComplete = true; frameComplete = true;
} else { } else {
/* This shouldn't happen */ /* This shouldn't happen */
Error("Internal error. Attempting recovery"); Error("Internal error. Attempting recovery");
databuffer.consume(single_offsets.front()); databuffer.consume(single_offsets.front());
single_offsets.pop_front(); single_offsets.pop_front();
} }
} else { } else {
/* Don't have a frame yet, wait for the request complete condition variable */ /* Don't have a frame yet, wait for the request complete condition variable */
nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
if(nRet != 0) { if(nRet != 0) {
Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
return -19; return -19;
} }
} }
} else { } else {
/* Failed to match content-type */ /* Failed to match content-type */
Fatal("Unable to match Content-Type. Check URL, username and password"); Fatal("Unable to match Content-Type. Check URL, username and password");
} /* mode */ } /* mode */
} /* frameComplete loop */ } /* frameComplete loop */
/* Release the mutex */ /* Release the mutex */
unlock(); unlock();
if(!frameComplete) if(!frameComplete)
return -1; return -1;
return 0; return 0;
} }
int cURLCamera::PostCapture() int cURLCamera::PostCapture()
{ {
// Nothing to do here // Nothing to do here
return( 0 ); return( 0 );
} }
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
{ {
lock(); lock();
/* Append the data we just received to our buffer */ /* Append the data we just received to our buffer */
databuffer.append((const char*)buffer, size*nmemb); databuffer.append((const char*)buffer, size*nmemb);
/* Signal data available */ /* Signal data available */
nRet = pthread_cond_signal(&data_available_cond); nRet = pthread_cond_signal(&data_available_cond);
if(nRet != 0) { if(nRet != 0) {
Error("Failed signaling data available condition variable: %s",strerror(nRet)); Error("Failed signaling data available condition variable: %s",strerror(nRet));
return -16; return -16;
} }
unlock(); unlock();
/* Return bytes processed */ /* Return bytes processed */
return size*nmemb; return size*nmemb;
} }
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata)
{ {
std::string header; std::string header;
header.assign((const char*)buffer, size*nmemb); header.assign((const char*)buffer, size*nmemb);
Debug(4,"Got header: %s",header.c_str()); Debug(4,"Got header: %s",header.c_str());
/* Check Content-Type header */ /* Check Content-Type header */
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) { if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
size_t pos = header.find(';'); size_t pos = header.find(';');
if(pos != std::string::npos) { if(pos != std::string::npos) {
header.erase(pos, std::string::npos); header.erase(pos, std::string::npos);
} }
pos = header.rfind(' '); pos = header.rfind(' ');
if(pos == std::string::npos) { if(pos == std::string::npos) {
pos = header.find(':'); pos = header.find(':');
} }
std::string content_type = header.substr(pos+1, std::string::npos); std::string content_type = header.substr(pos+1, std::string::npos);
Debug(6,"Content-Type is: %s",content_type.c_str()); Debug(6,"Content-Type is: %s",content_type.c_str());
lock(); lock();
const char* multipart_match = "multipart/x-mixed-replace"; const char* multipart_match = "multipart/x-mixed-replace";
const char* image_jpeg_match = "image/jpeg"; const char* image_jpeg_match = "image/jpeg";
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) { if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
Debug(7,"Content type matched as multipart/x-mixed-replace"); Debug(7,"Content type matched as multipart/x-mixed-replace");
mode = MODE_STREAM; mode = MODE_STREAM;
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) { } else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
Debug(7,"Content type matched as image/jpeg"); Debug(7,"Content type matched as image/jpeg");
mode = MODE_SINGLE; mode = MODE_SINGLE;
} }
unlock(); unlock();
} }
/* Return bytes processed */ /* Return bytes processed */
return size*nmemb; return size*nmemb;
} }
void* cURLCamera::thread_func() void* cURLCamera::thread_func()
{ {
long tRet; long tRet;
double dSize; double dSize;
c = curl_easy_init(); c = curl_easy_init();
if(c == NULL) { if(c == NULL) {
Fatal("Failed getting easy handle from libcurl"); Fatal("Failed getting easy handle from libcurl");
} }
/* Set URL */ /* Set URL */
cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str());
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet));
/* Header callback */ /* Header callback */
cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet));
cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet));
/* Data callback */ /* Data callback */
cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet));
cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet));
/* Progress callback */ /* Progress callback */
cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet));
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet));
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet));
/* Set username and password */ /* Set username and password */
if(!mUser.empty()) { if(!mUser.empty()) {
cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str());
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Error("Failed setting username: %s", curl_easy_strerror(cRet)); Error("Failed setting username: %s", curl_easy_strerror(cRet));
} }
if(!mPass.empty()) { if(!mPass.empty()) {
cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str());
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Error("Failed setting password: %s", curl_easy_strerror(cRet)); Error("Failed setting password: %s", curl_easy_strerror(cRet));
} }
/* Authenication preference */ /* Authenication preference */
cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
if(cRet != CURLE_OK) if(cRet != CURLE_OK)
Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet));
/* Work loop */ /* Work loop */
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) { for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
tRet = 0; tRet = 0;
while(!bTerminate) { while(!bTerminate) {
/* Do the work */ /* Do the work */
cRet = curl_easy_perform(c); cRet = curl_easy_perform(c);
if(mode == MODE_SINGLE) { if(mode == MODE_SINGLE) {
if(cRet != CURLE_OK) { if(cRet != CURLE_OK) {
break; break;
} }
/* Attempt to get the size of the file */ /* Attempt to get the size of the file */
cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
if(cRet != CURLE_OK) { if(cRet != CURLE_OK) {
break; break;
} }
/* We need to lock for the offsets array and the condition variable */ /* We need to lock for the offsets array and the condition variable */
lock(); lock();
/* Push the size into our offsets array */ /* Push the size into our offsets array */
if(dSize > 0) { if(dSize > 0) {
single_offsets.push_back(dSize); single_offsets.push_back(dSize);
} else { } else {
Fatal("Unable to get the size of the image"); Fatal("Unable to get the size of the image");
} }
/* Signal the request complete condition variable */ /* Signal the request complete condition variable */
tRet = pthread_cond_signal(&request_complete_cond); tRet = pthread_cond_signal(&request_complete_cond);
if(tRet != 0) { if(tRet != 0) {
Error("Failed signaling request completed condition variable: %s",strerror(tRet)); Error("Failed signaling request completed condition variable: %s",strerror(tRet));
} }
/* Unlock */ /* Unlock */
unlock(); unlock();
} else if (mode == MODE_STREAM) { } else if (mode == MODE_STREAM) {
break; break;
} }
} }
/* Return value checking */ /* Return value checking */
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) { if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
/* Aborted */ /* Aborted */
break; break;
} else if (cRet != CURLE_OK) { } else if (cRet != CURLE_OK) {
/* Some error */ /* Some error */
Error("cURL Request failed: %s",curl_easy_strerror(cRet)); Error("cURL Request failed: %s",curl_easy_strerror(cRet));
if(attempt < CURL_MAXRETRY) { if(attempt < CURL_MAXRETRY) {
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
/* Do a reset */ /* Do a reset */
lock(); lock();
databuffer.clear(); databuffer.clear();
single_offsets.clear(); single_offsets.clear();
mode = MODE_UNSET; mode = MODE_UNSET;
bReset = true; bReset = true;
unlock(); unlock();
} }
tRet = -50; tRet = -50;
} }
} }
/* Cleanup */ /* Cleanup */
curl_easy_cleanup(c); curl_easy_cleanup(c);
c = NULL; c = NULL;
return (void*)tRet; return (void*)tRet;
} }
int cURLCamera::lock() { int cURLCamera::lock() {
int nRet; int nRet;
/* Lock shared data */ /* Lock shared data */
nRet = pthread_mutex_lock(&shareddata_mutex); nRet = pthread_mutex_lock(&shareddata_mutex);
if(nRet != 0) { if(nRet != 0) {
Error("Failed locking shared data mutex: %s",strerror(nRet)); Error("Failed locking shared data mutex: %s",strerror(nRet));
} }
return nRet; return nRet;
} }
int cURLCamera::unlock() { int cURLCamera::unlock() {
int nRet; int nRet;
/* Unlock shared data */ /* Unlock shared data */
nRet = pthread_mutex_unlock(&shareddata_mutex); nRet = pthread_mutex_unlock(&shareddata_mutex);
if(nRet != 0) { if(nRet != 0) {
Error("Failed unlocking shared data mutex: %s",strerror(nRet)); Error("Failed unlocking shared data mutex: %s",strerror(nRet));
} }
return nRet; return nRet;
} }
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
{ {
/* Signal the curl thread to terminate */ /* Signal the curl thread to terminate */
if(bTerminate) if(bTerminate)
return -10; return -10;
return 0; return 0;
} }
/* These functions call the functions in the class for the correct object */ /* These functions call the functions in the class for the correct object */
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
{ {
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata); return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
} }
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
{ {
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata); return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
} }
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
{ {
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow); return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
} }
void* thread_func_dispatcher(void* object) { void* thread_func_dispatcher(void* object) {
return ((cURLCamera*)object)->thread_func(); return ((cURLCamera*)object)->thread_func();
} }

View File

@ -42,55 +42,55 @@
class cURLCamera : public Camera class cURLCamera : public Camera
{ {
protected: protected:
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t; typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
std::string mPath; std::string mPath;
std::string mUser; std::string mUser;
std::string mPass; std::string mPass;
/* cURL object(s) */ /* cURL object(s) */
CURL* c; CURL* c;
/* Shared data */ /* Shared data */
volatile bool bTerminate; volatile bool bTerminate;
volatile bool bReset; volatile bool bReset;
volatile mode_t mode; volatile mode_t mode;
Buffer databuffer; Buffer databuffer;
std::deque<size_t> single_offsets; std::deque<size_t> single_offsets;
/* pthread objects */ /* pthread objects */
pthread_t thread; pthread_t thread;
pthread_mutex_t shareddata_mutex; pthread_mutex_t shareddata_mutex;
pthread_cond_t data_available_cond; pthread_cond_t data_available_cond;
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 );
~cURLCamera(); ~cURLCamera();
const std::string &Path() const { return( mPath ); } const std::string &Path() const { return( mPath ); }
const std::string &Username() const { return( mUser ); } const std::string &Username() const { return( mUser ); }
const std::string &Password() const { return( mPass ); } const std::string &Password() const { return( mPass ); }
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata); size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow); int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data); int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data);
void* thread_func(); void* thread_func();
int lock(); int lock();
int unlock(); int unlock();
private: private:
int nRet; int nRet;
CURLcode cRet; CURLcode cRet;
}; };

View File

@ -29,85 +29,85 @@ int zmDbConnected = false;
void zmDbConnect() void zmDbConnect()
{ {
if ( !mysql_init( &dbconn ) ) if ( !mysql_init( &dbconn ) )
{ {
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
my_bool reconnect = 1; my_bool reconnect = 1;
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
if ( colonIndex != std::string::npos ) if ( colonIndex != std::string::npos )
{
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
{ {
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); exit( mysql_errno( &dbconn ) );
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
{
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
} }
else }
else
{
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
{ {
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
{ exit( mysql_errno( &dbconn ) );
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
} }
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) }
{ if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
Error( "Can't select database: %s", mysql_error( &dbconn ) ); {
exit( mysql_errno( &dbconn ) ); Error( "Can't select database: %s", mysql_error( &dbconn ) );
} exit( mysql_errno( &dbconn ) );
zmDbConnected = true; }
zmDbConnected = true;
} }
void zmDbClose() void zmDbClose()
{ {
if ( zmDbConnected ) if ( zmDbConnected )
{ {
mysql_close( &dbconn ); mysql_close( &dbconn );
// mysql_init() call implicitly mysql_library_init() but // mysql_init() call implicitly mysql_library_init() but
// mysql_close() does not call mysql_library_end() // mysql_close() does not call mysql_library_end()
mysql_library_end(); mysql_library_end();
zmDbConnected = false; zmDbConnected = false;
} }
} }
MYSQL_RES * zmDbFetch( const char * query ) { MYSQL_RES * zmDbFetch( const char * query ) {
if ( ! zmDbConnected ) { if ( ! zmDbConnected ) {
Error( "Not connected." ); Error( "Not connected." );
return NULL; return NULL;
} }
if ( mysql_query( &dbconn, query ) ) { if ( mysql_query( &dbconn, query ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
return NULL; return NULL;
} }
Debug( 4, "Success running query: %s", query ); Debug( 4, "Success running query: %s", query );
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query ); Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
return NULL; return NULL;
} }
return result; return result;
} // end MYSQL_RES * zmDbFetch( const char * query ); } // end MYSQL_RES * zmDbFetch( const char * query );
MYSQL_ROW zmDbFetchOne( const char *query ) { MYSQL_ROW zmDbFetchOne( const char *query ) {
MYSQL_RES *result = zmDbFetch( query ); MYSQL_RES *result = zmDbFetch( query );
int n_rows = mysql_num_rows( result ); int n_rows = mysql_num_rows( result );
if ( n_rows != 1 ) { if ( n_rows != 1 ) {
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query ); Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
return NULL; return NULL;
} }
MYSQL_ROW dbrow = mysql_fetch_row( result ); MYSQL_ROW dbrow = mysql_fetch_row( result );
mysql_free_result( result ); mysql_free_result( result );
if ( ! dbrow ) { if ( ! dbrow ) {
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) ); Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
return NULL; return NULL;
} }
return dbrow; return dbrow;
} }

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,7 @@
class Zone; class Zone;
class Monitor; class Monitor;
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored #define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
// //
// Class describing events, i.e. captured periods of activity. // Class describing events, i.e. captured periods of activity.
@ -51,219 +51,219 @@ class Event
friend class EventStream; friend class EventStream;
protected: protected:
static bool initialised; static bool initialised;
static char capture_file_format[PATH_MAX]; static char capture_file_format[PATH_MAX];
static char analyse_file_format[PATH_MAX]; static char analyse_file_format[PATH_MAX];
static char general_file_format[PATH_MAX]; static char general_file_format[PATH_MAX];
protected: protected:
static int sd; static int sd;
public: public:
typedef std::set<std::string> StringSet; typedef std::set<std::string> StringSet;
typedef std::map<std::string,StringSet> StringSetMap; typedef std::map<std::string,StringSet> StringSetMap;
protected: protected:
typedef enum { NORMAL, BULK, ALARM } FrameType; typedef enum { NORMAL, BULK, ALARM } FrameType;
struct PreAlarmData struct PreAlarmData
{ {
Image *image; Image *image;
struct timeval timestamp; struct timeval timestamp;
unsigned int score; unsigned int score;
Image *alarm_frame; Image *alarm_frame;
}; };
static int pre_alarm_count; static int pre_alarm_count;
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
protected: protected:
unsigned int id; unsigned int id;
Monitor *monitor; Monitor *monitor;
struct timeval start_time; struct timeval start_time;
struct timeval end_time; struct timeval end_time;
std::string cause; std::string cause;
StringSetMap noteSetMap; StringSetMap noteSetMap;
int frames; int frames;
int alarm_frames; int alarm_frames;
unsigned int tot_score; unsigned int tot_score;
unsigned int max_score; unsigned int max_score;
char path[PATH_MAX]; char path[PATH_MAX];
protected: protected:
int last_db_frame; int last_db_frame;
protected: protected:
static void Initialise() static void Initialise()
{ {
if ( initialised ) if ( initialised )
return; return;
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
initialised = true; initialised = true;
} }
void createNotes( std::string &notes ); void createNotes( std::string &notes );
public: public:
static bool OpenFrameSocket( int ); static bool OpenFrameSocket( int );
static bool ValidateFrameSocket( int ); static bool ValidateFrameSocket( int );
public: public:
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
~Event(); ~Event();
int Id() const { return( id ); } int Id() const { return( id ); }
const std::string &Cause() { return( cause ); } const std::string &Cause() { return( cause ); }
int Frames() const { return( frames ); } int Frames() const { return( frames ); }
int AlarmFrames() const { return( alarm_frames ); } int AlarmFrames() const { return( alarm_frames ); }
const struct timeval &StartTime() const { return( start_time ); } const struct timeval &StartTime() const { return( start_time ); }
const struct timeval &EndTime() const { return( end_time ); } const struct timeval &EndTime() const { return( end_time ); }
struct timeval &EndTime() { return( end_time ); } struct timeval &EndTime() { return( end_time ); }
bool SendFrameImage( const Image *image, bool alarm_frame=false ); bool SendFrameImage( const Image *image, bool alarm_frame=false );
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
void updateNotes( const StringSetMap &stringSetMap ); void updateNotes( const StringSetMap &stringSetMap );
void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
private: private:
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
public: public:
static const char *getSubPath( struct tm *time ) static const char *getSubPath( struct tm *time )
{ {
static char subpath[PATH_MAX] = ""; static char subpath[PATH_MAX] = "";
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
return( subpath ); return( subpath );
} }
static const char *getSubPath( time_t *time ) static const char *getSubPath( time_t *time )
{ {
return( Event::getSubPath( localtime( time ) ) ); return( Event::getSubPath( localtime( time ) ) );
} }
public: public:
static int PreAlarmCount() static int PreAlarmCount()
{ {
return( pre_alarm_count ); return( pre_alarm_count );
} }
static void EmptyPreAlarmFrames() static void EmptyPreAlarmFrames()
{ {
if ( pre_alarm_count > 0 ) if ( pre_alarm_count > 0 )
{ {
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
{ {
delete pre_alarm_data[i].image; delete pre_alarm_data[i].image;
delete pre_alarm_data[i].alarm_frame; delete pre_alarm_data[i].alarm_frame;
} }
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
} }
pre_alarm_count = 0; pre_alarm_count = 0;
} }
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
{ {
pre_alarm_data[pre_alarm_count].image = new Image( *image ); pre_alarm_data[pre_alarm_count].image = new Image( *image );
pre_alarm_data[pre_alarm_count].timestamp = timestamp; pre_alarm_data[pre_alarm_count].timestamp = timestamp;
pre_alarm_data[pre_alarm_count].score = score; pre_alarm_data[pre_alarm_count].score = score;
if ( alarm_frame ) if ( alarm_frame )
{ {
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
} }
pre_alarm_count++; pre_alarm_count++;
} }
void SavePreAlarmFrames() void SavePreAlarmFrames()
{ {
for ( int i = 0; i < pre_alarm_count; i++ ) for ( int i = 0; i < pre_alarm_count; i++ )
{ {
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
} }
EmptyPreAlarmFrames(); EmptyPreAlarmFrames();
} }
}; };
class EventStream : public StreamBase class EventStream : public StreamBase
{ {
public: public:
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
protected: protected:
struct FrameData { struct FrameData {
//unsigned long id; //unsigned long id;
time_t timestamp; time_t timestamp;
time_t offset; time_t offset;
double delta; double delta;
bool in_db; bool in_db;
}; };
struct EventData struct EventData
{ {
unsigned long event_id; unsigned long event_id;
unsigned long monitor_id; unsigned long monitor_id;
unsigned long frame_count; unsigned long frame_count;
time_t start_time; time_t start_time;
double duration; double duration;
char path[PATH_MAX]; char path[PATH_MAX];
int n_frames; int n_frames;
FrameData *frames; FrameData *frames;
}; };
protected: protected:
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
static const StreamMode DEFAULT_MODE = MODE_SINGLE; static const StreamMode DEFAULT_MODE = MODE_SINGLE;
protected: protected:
StreamMode mode; StreamMode mode;
bool forceEventChange; bool forceEventChange;
protected: protected:
int curr_frame_id; int curr_frame_id;
double curr_stream_time; double curr_stream_time;
EventData *event_data; EventData *event_data;
protected: protected:
bool loadEventData( int event_id ); bool loadEventData( int event_id );
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( int init_event_id, unsigned int init_frame_id );
bool loadInitialEventData( int monitor_id, time_t event_time ); bool loadInitialEventData( int monitor_id, time_t event_time );
void checkEventLoaded(); void checkEventLoaded();
void processCommand( const CmdMsg *msg ); void processCommand( const CmdMsg *msg );
bool sendFrame( int delta_us ); bool sendFrame( int delta_us );
public: public:
EventStream() EventStream()
{ {
mode = DEFAULT_MODE; mode = DEFAULT_MODE;
forceEventChange = false; forceEventChange = false;
curr_frame_id = 0; curr_frame_id = 0;
curr_stream_time = 0.0; curr_stream_time = 0.0;
event_data = 0; event_data = 0;
} }
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) void setStreamStart( int init_event_id, unsigned int init_frame_id=0 )
{ {
loadInitialEventData( init_event_id, init_frame_id ); loadInitialEventData( init_event_id, init_frame_id );
loadMonitor( event_data->monitor_id ); loadMonitor( event_data->monitor_id );
} }
void setStreamStart( int monitor_id, time_t event_time ) void setStreamStart( int monitor_id, time_t event_time )
{ {
loadInitialEventData( monitor_id, event_time ); loadInitialEventData( monitor_id, event_time );
loadMonitor( monitor_id ); loadMonitor( monitor_id );
} }
void setStreamMode( StreamMode p_mode ) void setStreamMode( StreamMode p_mode )
{ {
mode = p_mode; mode = p_mode;
} }
void runStream(); void runStream();
}; };
#endif // ZM_EVENT_H #endif // ZM_EVENT_H

View File

@ -27,42 +27,42 @@
class Exception class Exception
{ {
protected: protected:
typedef enum { INFO, WARNING, ERROR, FATAL } Severity; typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
protected: protected:
std::string mMessage; std::string mMessage;
Severity mSeverity; Severity mSeverity;
public: public:
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity ) Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
{ {
} }
public: public:
const std::string &getMessage() const const std::string &getMessage() const
{ {
return( mMessage ); return( mMessage );
} }
Severity getSeverity() const Severity getSeverity() const
{ {
return( mSeverity ); return( mSeverity );
} }
bool isInfo() const bool isInfo() const
{ {
return( mSeverity == INFO ); return( mSeverity == INFO );
} }
bool isWarning() const bool isWarning() const
{ {
return( mSeverity == WARNING ); return( mSeverity == WARNING );
} }
bool isError() const bool isError() const
{ {
return( mSeverity == ERROR ); return( mSeverity == ERROR );
} }
bool isFatal() const bool isFatal() const
{ {
return( mSeverity == FATAL ); return( mSeverity == FATAL );
} }
}; };
#endif // ZM_EXCEPTION_H #endif // ZM_EXCEPTION_H

View File

@ -25,210 +25,210 @@
#if HAVE_LIBAVUTIL #if HAVE_LIBAVUTIL
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) { enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
enum _AVPIXELFORMAT pf; enum _AVPIXELFORMAT pf;
Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder);
switch(p_colours) { switch(p_colours) {
case ZM_COLOUR_RGB24: case ZM_COLOUR_RGB24:
{ {
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
/* BGR subpixel order */ /* BGR subpixel order */
pf = AV_PIX_FMT_BGR24; pf = AV_PIX_FMT_BGR24;
} else { } else {
/* Assume RGB subpixel order */ /* Assume RGB subpixel order */
pf = AV_PIX_FMT_RGB24; pf = AV_PIX_FMT_RGB24;
} }
break; break;
} }
case ZM_COLOUR_RGB32: case ZM_COLOUR_RGB32:
{ {
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
/* ARGB subpixel order */ /* ARGB subpixel order */
pf = AV_PIX_FMT_ARGB; pf = AV_PIX_FMT_ARGB;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
/* ABGR subpixel order */ /* ABGR subpixel order */
pf = AV_PIX_FMT_ABGR; pf = AV_PIX_FMT_ABGR;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
/* BGRA subpixel order */ /* BGRA subpixel order */
pf = AV_PIX_FMT_BGRA; pf = AV_PIX_FMT_BGRA;
} else { } else {
/* Assume RGBA subpixel order */ /* Assume RGBA subpixel order */
pf = AV_PIX_FMT_RGBA; pf = AV_PIX_FMT_RGBA;
} }
break; break;
} }
case ZM_COLOUR_GRAY8: case ZM_COLOUR_GRAY8:
pf = AV_PIX_FMT_GRAY8; pf = AV_PIX_FMT_GRAY8;
break; break;
default: default:
Panic("Unexpected colours: %d",p_colours); Panic("Unexpected colours: %d",p_colours);
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
break; break;
} }
return pf; return pf;
} }
#endif // HAVE_LIBAVUTIL #endif // HAVE_LIBAVUTIL
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) {
Debug(4,"SWScale object created"); Debug(4,"SWScale object created");
/* Allocate AVFrame for the input */ /* Allocate AVFrame for the input */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
input_avframe = av_frame_alloc(); input_avframe = av_frame_alloc();
#else #else
input_avframe = avcodec_alloc_frame(); input_avframe = avcodec_alloc_frame();
#endif #endif
if(input_avframe == NULL) { if(input_avframe == NULL) {
Fatal("Failed allocating AVFrame for the input"); Fatal("Failed allocating AVFrame for the input");
} }
/* Allocate AVFrame for the output */ /* Allocate AVFrame for the output */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
output_avframe = av_frame_alloc(); output_avframe = av_frame_alloc();
#else #else
output_avframe = avcodec_alloc_frame(); output_avframe = avcodec_alloc_frame();
#endif #endif
if(output_avframe == NULL) { if(output_avframe == NULL) {
Fatal("Failed allocating AVFrame for the output"); Fatal("Failed allocating AVFrame for the output");
} }
} }
SWScale::~SWScale() { SWScale::~SWScale() {
/* Free up everything */ /* Free up everything */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &input_avframe ); av_frame_free( &input_avframe );
#else #else
av_freep( &input_avframe ); av_freep( &input_avframe );
#endif #endif
//input_avframe = NULL; //input_avframe = NULL;
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &output_avframe ); av_frame_free( &output_avframe );
#else #else
av_freep( &output_avframe ); av_freep( &output_avframe );
#endif #endif
//output_avframe = NULL; //output_avframe = NULL;
if(swscale_ctx) { if(swscale_ctx) {
sws_freeContext(swscale_ctx); sws_freeContext(swscale_ctx);
swscale_ctx = NULL; swscale_ctx = NULL;
} }
Debug(4,"SWScale object destroyed"); Debug(4,"SWScale object destroyed");
} }
int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
/* Assign the defaults */ /* Assign the defaults */
default_input_pf = in_pf; default_input_pf = in_pf;
default_output_pf = out_pf; default_output_pf = out_pf;
default_width = width; default_width = width;
default_height = height; default_height = height;
gotdefaults = true; gotdefaults = true;
return 0; return 0;
} }
int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
/* Parameter checking */ /* Parameter checking */
if(in_buffer == NULL || out_buffer == NULL) { if(in_buffer == NULL || out_buffer == NULL) {
Error("NULL Input or output buffer"); Error("NULL Input or output buffer");
return -1; return -1;
} }
if(in_pf == 0 || out_pf == 0) { if(in_pf == 0 || out_pf == 0) {
Error("Invalid input or output pixel formats"); Error("Invalid input or output pixel formats");
return -2; return -2;
} }
if(!width || !height) { if(!width || !height) {
Error("Invalid width or height"); Error("Invalid width or height");
return -3; return -3;
} }
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) #if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
/* Warn if the input or output pixelformat is not supported */ /* Warn if the input or output pixelformat is not supported */
if(!sws_isSupportedInput(in_pf)) { if(!sws_isSupportedInput(in_pf)) {
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
} }
if(!sws_isSupportedOutput(out_pf)) { if(!sws_isSupportedOutput(out_pf)) {
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
} }
#endif #endif
/* Check the buffer sizes */ /* Check the buffer sizes */
size_t insize = avpicture_get_size(in_pf, width, height); size_t insize = avpicture_get_size(in_pf, width, height);
if(insize != in_buffer_size) { if(insize != in_buffer_size) {
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
return -4; return -4;
} }
size_t outsize = avpicture_get_size(out_pf, width, height); size_t outsize = avpicture_get_size(out_pf, width, height);
if(outsize < out_buffer_size) { if(outsize < out_buffer_size) {
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
return -5; return -5;
} }
/* Get the context */ /* Get the context */
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL ); swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
if(swscale_ctx == NULL) { if(swscale_ctx == NULL) {
Error("Failed getting swscale context"); Error("Failed getting swscale context");
return -6; return -6;
} }
/* Fill in the buffers */ /* Fill in the buffers */
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) { if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
Error("Failed filling input frame with input buffer"); Error("Failed filling input frame with input buffer");
return -7; return -7;
} }
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) { if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
Error("Failed filling output frame with output buffer"); Error("Failed filling output frame with output buffer");
return -8; return -8;
} }
/* Do the conversion */ /* Do the conversion */
if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) {
Error("swscale conversion failed"); Error("swscale conversion failed");
return -10; return -10;
} }
return 0; return 0;
} }
int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
if(img->Width() != width) { if(img->Width() != width) {
Error("Source image width differs. Source: %d Output: %d",img->Width(), width); Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
return -12; return -12;
} }
if(img->Height() != height) { if(img->Height() != height) {
Error("Source image height differs. Source: %d Output: %d",img->Height(), height); Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
return -13; return -13;
} }
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height);
} }
int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) {
if(!gotdefaults) { if(!gotdefaults) {
Error("Defaults are not set"); Error("Defaults are not set");
return -24; return -24;
} }
return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
} }
int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) {
if(!gotdefaults) { if(!gotdefaults) {
Error("Defaults are not set"); Error("Defaults are not set");
return -24; return -24;
} }
return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
} }
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL

View File

@ -39,8 +39,8 @@ extern "C" {
* b and c the minor and micro versions of libav * b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */ * d and e the minor and micro versions of FFmpeg */
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \ #define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0) #if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
#include <libavutil/opt.h> #include <libavutil/opt.h>
@ -53,55 +53,55 @@ extern "C" {
#include <ffmpeg/mathematics.h> #include <ffmpeg/mathematics.h>
#include <ffmpeg/opt.h> #include <ffmpeg/opt.h>
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ #endif /* HAVE_LIBAVUTIL_AVUTIL_H */
#if defined(HAVE_LIBAVUTIL_AVUTIL_H) #if defined(HAVE_LIBAVUTIL_AVUTIL_H)
#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100) #if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100)
#define _AVPIXELFORMAT AVPixelFormat #define _AVPIXELFORMAT AVPixelFormat
#else #else
#define _AVPIXELFORMAT PixelFormat #define _AVPIXELFORMAT PixelFormat
#define AV_PIX_FMT_NONE PIX_FMT_NONE #define AV_PIX_FMT_NONE PIX_FMT_NONE
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444 #define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555 #define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 #define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 #define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA #define AV_PIX_FMT_BGRA PIX_FMT_BGRA
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB #define AV_PIX_FMT_ARGB PIX_FMT_ARGB
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR #define AV_PIX_FMT_ABGR PIX_FMT_ABGR
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA #define AV_PIX_FMT_RGBA PIX_FMT_RGBA
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8 #define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422 #define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P #define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P #define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P #define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P #define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P #define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P #define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P #define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411 #define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555 #define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8 #define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4 #define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE #define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8 #define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4 #define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE #define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
#define AV_PIX_FMT_NV12 PIX_FMT_NV12 #define AV_PIX_FMT_NV12 PIX_FMT_NV12
#define AV_PIX_FMT_NV21 PIX_FMT_NV21 #define AV_PIX_FMT_NV21 PIX_FMT_NV21
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1 #define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1 #define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE #define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE #define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P #define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P #define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P #define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264 //#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1 //#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2 //#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
#endif #endif
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ #endif /* HAVE_LIBAVUTIL_AVUTIL_H */
@ -116,8 +116,8 @@ extern "C" {
* b and c the minor and micro versions of libav * b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */ * d and e the minor and micro versions of FFmpeg */
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \ #define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVCODEC_H #elif HAVE_FFMPEG_AVCODEC_H
#include <ffmpeg/avcodec.h> #include <ffmpeg/avcodec.h>
@ -125,9 +125,9 @@ extern "C" {
#if defined(HAVE_LIBAVCODEC_AVCODEC_H) #if defined(HAVE_LIBAVCODEC_AVCODEC_H)
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100) #if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
#define _AVCODECID AVCodecID #define _AVCODECID AVCodecID
#else #else
#define _AVCODECID CodecID #define _AVCODECID CodecID
#endif #endif
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */ #endif /* HAVE_LIBAVCODEC_AVCODEC_H */
@ -141,8 +141,8 @@ extern "C" {
* b and c the minor and micro versions of libav * b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */ * d and e the minor and micro versions of FFmpeg */
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \ #define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVFORMAT_H #elif HAVE_FFMPEG_AVFORMAT_H
#include <ffmpeg/avformat.h> #include <ffmpeg/avformat.h>
@ -157,8 +157,8 @@ extern "C" {
* b and c the minor and micro versions of libav * b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */ * d and e the minor and micro versions of FFmpeg */
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \ #define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ ( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) (LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVDEVICE_H #elif HAVE_FFMPEG_AVDEVICE_H
#include <ffmpeg/avdevice.h> #include <ffmpeg/avdevice.h>
@ -173,8 +173,8 @@ extern "C" {
* b and c the minor and micro versions of libav * b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */ * d and e the minor and micro versions of FFmpeg */
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \ #define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ ( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) (LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_SWSCALE_H #elif HAVE_FFMPEG_SWSCALE_H
#include <ffmpeg/swscale.h> #include <ffmpeg/swscale.h>
@ -203,23 +203,23 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
class SWScale { class SWScale {
public: public:
SWScale(); SWScale();
~SWScale(); ~SWScale();
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
protected: protected:
bool gotdefaults; bool gotdefaults;
struct SwsContext* swscale_ctx; struct SwsContext* swscale_ctx;
AVFrame* input_avframe; AVFrame* input_avframe;
AVFrame* output_avframe; AVFrame* output_avframe;
enum _AVPIXELFORMAT default_input_pf; enum _AVPIXELFORMAT default_input_pf;
enum _AVPIXELFORMAT default_output_pf; enum _AVPIXELFORMAT default_output_pf;
unsigned int default_width; unsigned int default_width;
unsigned int default_height; unsigned int default_height;
}; };
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
@ -256,21 +256,21 @@ protected:
*/ */
#ifdef __cplusplus #ifdef __cplusplus
inline static const std::string av_make_error_string(int errnum) inline static const std::string av_make_error_string(int errnum)
{ {
char errbuf[AV_ERROR_MAX_STRING_SIZE]; char errbuf[AV_ERROR_MAX_STRING_SIZE];
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) #if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
#else #else
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum); snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
#endif #endif
return (std::string)errbuf; return (std::string)errbuf;
} }
#undef av_err2str #undef av_err2str
#define av_err2str(errnum) av_make_error_string(errnum).c_str() #define av_err2str(errnum) av_make_error_string(errnum).c_str()
#endif // __cplusplus #endif // __cplusplus
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) #endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )

View File

@ -28,71 +28,71 @@
#endif #endif
#ifdef SOLARIS #ifdef SOLARIS
#include <sys/errno.h> // for ESRCH #include <sys/errno.h> // for ESRCH
#include <signal.h> #include <signal.h>
#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 ) :
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 ),
mPath( p_path ), mPath( p_path ),
mMethod( p_method ), mMethod( p_method ),
mOptions( p_options ) mOptions( p_options )
{ {
if ( capture ) if ( capture )
{ {
Initialise(); Initialise();
} }
mFormatContext = NULL; mFormatContext = NULL;
mVideoStreamId = -1; mVideoStreamId = -1;
mCodecContext = NULL; mCodecContext = NULL;
mCodec = NULL; mCodec = NULL;
mRawFrame = NULL; mRawFrame = NULL;
mFrame = NULL; mFrame = NULL;
frameCount = 0; frameCount = 0;
mIsOpening = false; mIsOpening = false;
mCanCapture = false; mCanCapture = false;
mOpenStart = 0; mOpenStart = 0;
mReopenThread = 0; mReopenThread = 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 */
if(colours == ZM_COLOUR_RGB32) { if(colours == ZM_COLOUR_RGB32) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA; subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA; imagePixFormat = AV_PIX_FMT_RGBA;
} else if(colours == ZM_COLOUR_RGB24) { } else if(colours == ZM_COLOUR_RGB24) {
subpixelorder = ZM_SUBPIX_ORDER_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB;
imagePixFormat = AV_PIX_FMT_RGB24; imagePixFormat = AV_PIX_FMT_RGB24;
} else if(colours == ZM_COLOUR_GRAY8) { } else if(colours == ZM_COLOUR_GRAY8) {
subpixelorder = ZM_SUBPIX_ORDER_NONE; subpixelorder = ZM_SUBPIX_ORDER_NONE;
imagePixFormat = AV_PIX_FMT_GRAY8; imagePixFormat = AV_PIX_FMT_GRAY8;
} else { } else {
Panic("Unexpected colours: %d",colours); Panic("Unexpected colours: %d",colours);
} }
} }
FfmpegCamera::~FfmpegCamera() FfmpegCamera::~FfmpegCamera()
{ {
CloseFfmpeg(); CloseFfmpeg();
if ( capture ) if ( capture )
{ {
Terminate(); Terminate();
} }
} }
void FfmpegCamera::Initialise() void FfmpegCamera::Initialise()
{ {
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();
} }
void FfmpegCamera::Terminate() void FfmpegCamera::Terminate()
@ -101,373 +101,373 @@ void FfmpegCamera::Terminate()
int FfmpegCamera::PrimeCapture() int FfmpegCamera::PrimeCapture()
{ {
Info( "Priming capture from %s", mPath.c_str() ); Info( "Priming capture from %s", mPath.c_str() );
if (OpenFfmpeg() != 0){ if (OpenFfmpeg() != 0){
ReopenFfmpeg(); ReopenFfmpeg();
} }
return 0; return 0;
} }
int FfmpegCamera::PreCapture() int FfmpegCamera::PreCapture()
{ {
// Nothing to do here // Nothing to do here
return( 0 ); return( 0 );
} }
int FfmpegCamera::Capture( Image &image ) int FfmpegCamera::Capture( Image &image )
{ {
if (!mCanCapture){ if (!mCanCapture){
return -1; return -1;
}
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if (mReopenThread != 0) {
void *retval = 0;
int ret;
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
} }
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. Info( "Successfully reopened stream." );
if (mReopenThread != 0) { mReopenThread = 0;
void *retval = 0; }
int ret;
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
Info( "Successfully reopened stream." );
mReopenThread = 0;
}
AVPacket packet; AVPacket packet;
uint8_t* directbuffer; uint8_t* directbuffer;
/* 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 );
if ( avResult < 0 )
{ {
int avResult = av_read_frame( mFormatContext, &packet ); char errbuf[AV_ERROR_MAX_STRING_SIZE];
if ( avResult < 0 ) av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
{ if (
char errbuf[AV_ERROR_MAX_STRING_SIZE]; // Check if EOF.
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
if ( // Check for Connection failure.
// Check if EOF. (avResult == -110)
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || )
// Check for Connection failure. {
(avResult == -110) Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
) ReopenFfmpeg();
{ }
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
ReopenFfmpeg();
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
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
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
#endif #endif
Fatal( "Unable to decode frame at frame %d", frameCount ); Fatal( "Unable to decode frame at frame %d", frameCount );
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++;
} }
}
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
av_packet_unref( &packet);
#else
av_free_packet( &packet );
#endif
} }
return (0); #if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
av_packet_unref( &packet);
#else
av_free_packet( &packet );
#endif
}
return (0);
} }
int FfmpegCamera::PostCapture() int FfmpegCamera::PostCapture()
{ {
// Nothing to do here // Nothing to do here
return( 0 ); return( 0 );
} }
int FfmpegCamera::OpenFfmpeg() { int FfmpegCamera::OpenFfmpeg() {
Debug ( 2, "OpenFfmpeg called." ); Debug ( 2, "OpenFfmpeg called." );
mOpenStart = time(NULL); mOpenStart = time(NULL);
mIsOpening = true; mIsOpening = true;
// Open the input, not necessarily a file // Open the input, not necessarily a file
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
Debug ( 1, "Calling av_open_input_file" ); Debug ( 1, "Calling av_open_input_file" );
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 )
#else #else
// Handle options // Handle options
AVDictionary *opts = 0; AVDictionary *opts = 0;
StringVector opVect = split(Options(), ","); StringVector opVect = split(Options(), ",");
// Set transport method as specified by method field, rtpUni is default // Set transport method as specified by method field, rtpUni is default
if ( Method() == "rtpMulti" ) if ( Method() == "rtpMulti" )
opVect.push_back("rtsp_transport=udp_multicast"); opVect.push_back("rtsp_transport=udp_multicast");
else if ( Method() == "rtpRtsp" ) else if ( Method() == "rtpRtsp" )
opVect.push_back("rtsp_transport=tcp"); opVect.push_back("rtsp_transport=tcp");
else if ( Method() == "rtpRtspHttp" ) else if ( Method() == "rtpRtspHttp" )
opVect.push_back("rtsp_transport=http"); opVect.push_back("rtsp_transport=http");
Debug(2, "Number of Options: %d",opVect.size()); Debug(2, "Number of Options: %d",opVect.size());
for (size_t i=0; i<opVect.size(); i++) for (size_t i=0; i<opVect.size(); i++)
{ {
StringVector parts = split(opVect[i],"="); StringVector parts = split(opVect[i],"=");
if (parts.size() > 1) { if (parts.size() > 1) {
parts[0] = trimSpaces(parts[0]); parts[0] = trimSpaces(parts[0]);
parts[1] = trimSpaces(parts[1]); parts[1] = trimSpaces(parts[1]);
if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) { if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) {
Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str()); Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str());
} }
else else
{ {
Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() ); Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() );
} }
} }
else else
{ {
Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() ); Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() );
} }
} }
Debug ( 1, "Calling avformat_open_input" ); Debug ( 1, "Calling avformat_open_input" );
mFormatContext = avformat_alloc_context( ); mFormatContext = avformat_alloc_context( );
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
mFormatContext->interrupt_callback.opaque = this; mFormatContext->interrupt_callback.opaque = this;
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 ) if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 )
#endif #endif
{ {
mIsOpening = false;
Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) );
return -1;
}
AVDictionaryEntry *e;
if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) {
Warning( "Option %s not recognized by ffmpeg", e->key);
}
mIsOpening = false; mIsOpening = false;
Debug ( 1, "Opened input" ); Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) );
return -1;
}
// Locate stream info from avformat_open_input AVDictionaryEntry *e;
if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) {
Warning( "Option %s not recognized by ffmpeg", e->key);
}
mIsOpening = false;
Debug ( 1, "Opened input" );
// Locate stream info from avformat_open_input
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
Debug ( 1, "Calling av_find_stream_info" ); Debug ( 1, "Calling av_find_stream_info" );
if ( av_find_stream_info( mFormatContext ) < 0 ) if ( av_find_stream_info( mFormatContext ) < 0 )
#else #else
Debug ( 1, "Calling avformat_find_stream_info" ); Debug ( 1, "Calling avformat_find_stream_info" );
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 )
#endif #endif
Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) );
Debug ( 1, "Got stream info" ); Debug ( 1, "Got stream info" );
// Find first video stream present // Find first video stream present
mVideoStreamId = -1; mVideoStreamId = -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))
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 ) }
Fatal( "Unable to locate video stream in %s", mPath.c_str() ); if ( mVideoStreamId == -1 )
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
Debug ( 1, "Found video stream" ); Debug ( 1, "Found video stream" );
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
// Try and get the codec from the codec context // Try and get the codec from the codec context
if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); Fatal( "Can't find codec for video stream from %s", mPath.c_str() );
Debug ( 1, "Found decoder" ); Debug ( 1, "Found decoder" );
// Open the codec // Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug ( 1, "Calling avcodec_open" ); Debug ( 1, "Calling avcodec_open" );
if ( avcodec_open( mCodecContext, mCodec ) < 0 ) if ( avcodec_open( mCodecContext, mCodec ) < 0 )
#else #else
Debug ( 1, "Calling avcodec_open2" ); Debug ( 1, "Calling avcodec_open2" );
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
#endif #endif
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
Debug ( 1, "Opened codec" ); Debug ( 1, "Opened codec" );
// Allocate space for the native video frame // Allocate space for the native video frame
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #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)
Fatal( "Unable to allocate frame for %s", mPath.c_str() ); Fatal( "Unable to allocate frame for %s", mPath.c_str() );
Debug ( 1, "Allocated frames" ); Debug ( 1, "Allocated frames" );
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size( imagePixFormat, width, height );
if( (unsigned int)pSize != imagesize) { if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
} }
Debug ( 1, "Validated imagesize" ); Debug ( 1, "Validated imagesize" );
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
Debug ( 1, "Calling sws_isSupportedInput" ); Debug ( 1, "Calling sws_isSupportedInput" );
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
} }
if(!sws_isSupportedOutput(imagePixFormat)) { if(!sws_isSupportedOutput(imagePixFormat)) {
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
} }
#else // HAVE_LIBSWSCALE #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
mCanCapture = true; mCanCapture = true;
return 0; return 0;
} }
int FfmpegCamera::ReopenFfmpeg() { int FfmpegCamera::ReopenFfmpeg() {
Debug(2, "ReopenFfmpeg called."); Debug(2, "ReopenFfmpeg called.");
mCanCapture = false; mCanCapture = false;
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){ if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
// Log a fatal error and exit the process. // Log a fatal error and exit the process.
Fatal( "ReopenFfmpeg failed to create worker thread." ); Fatal( "ReopenFfmpeg failed to create worker thread." );
} }
return 0; return 0;
} }
int FfmpegCamera::CloseFfmpeg(){ int FfmpegCamera::CloseFfmpeg(){
Debug(2, "CloseFfmpeg called."); Debug(2, "CloseFfmpeg called.");
mCanCapture = false; mCanCapture = false;
#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 av_close_input_file mCodecContext = NULL; // Freed by av_close_input_file
} }
if ( mFormatContext ) if ( mFormatContext )
{ {
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( mFormatContext ); av_close_input_file( mFormatContext );
#else #else
avformat_close_input( &mFormatContext ); avformat_close_input( &mFormatContext );
#endif #endif
mFormatContext = NULL; mFormatContext = NULL;
} }
return 0; return 0;
} }
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
{ {
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx); FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
if (camera->mIsOpening){ if (camera->mIsOpening){
int now = time(NULL); int now = time(NULL);
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) { if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout ); Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
return 1; return 1;
}
} }
}
return 0; return 0;
} }
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
if (ctx == NULL) return NULL; if (ctx == NULL) return NULL;
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx); FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
while (1){ while (1){
// Close current stream. // Close current stream.
camera->CloseFfmpeg(); camera->CloseFfmpeg();
// Sleep if necessary to not reconnect too fast. // Sleep if necessary to not reconnect too fast.
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
wait = wait < 0 ? 0 : wait; wait = wait < 0 ? 0 : wait;
if (wait > 0){ if (wait > 0){
Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
sleep(wait); sleep(wait);
}
if (camera->OpenFfmpeg() == 0){
return NULL;
}
} }
if (camera->OpenFfmpeg() == 0){
return NULL;
}
}
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -33,51 +33,51 @@
class FfmpegCamera : public Camera class FfmpegCamera : public Camera
{ {
protected: protected:
std::string mPath; std::string mPath;
std::string mMethod; std::string mMethod;
std::string mOptions; std::string mOptions;
int frameCount; int frameCount;
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
AVFormatContext *mFormatContext; AVFormatContext *mFormatContext;
int mVideoStreamId; int mVideoStreamId;
AVCodecContext *mCodecContext; AVCodecContext *mCodecContext;
AVCodec *mCodec; AVCodec *mCodec;
AVFrame *mRawFrame; AVFrame *mRawFrame;
AVFrame *mFrame; AVFrame *mFrame;
_AVPIXELFORMAT imagePixFormat; _AVPIXELFORMAT imagePixFormat;
int OpenFfmpeg(); int OpenFfmpeg();
int ReopenFfmpeg(); int ReopenFfmpeg();
int CloseFfmpeg(); int CloseFfmpeg();
static int FfmpegInterruptCallback(void *ctx); static int FfmpegInterruptCallback(void *ctx);
static void* ReopenFfmpegThreadCallback(void *ctx); static void* ReopenFfmpegThreadCallback(void *ctx);
bool mIsOpening; bool mIsOpening;
bool mCanCapture; bool mCanCapture;
int mOpenStart; int mOpenStart;
pthread_t mReopenThread; pthread_t mReopenThread;
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
struct SwsContext *mConvertContext; struct SwsContext *mConvertContext;
#endif #endif
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 );
~FfmpegCamera(); ~FfmpegCamera();
const std::string &Path() const { return( mPath ); } const std::string &Path() const { return( mPath ); }
const std::string &Options() const { return( mOptions ); } const std::string &Options() const { return( mOptions ); }
const std::string &Method() const { return( mMethod ); } const std::string &Method() const { return( mMethod ); }
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
}; };
#endif // ZM_FFMPEG_CAMERA_H #endif // ZM_FFMPEG_CAMERA_H

View File

@ -36,28 +36,28 @@
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 ) : 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 )
{ {
strncpy( path, p_path, sizeof(path) ); strncpy( path, p_path, sizeof(path) );
if ( capture ) if ( capture )
{ {
Initialise(); Initialise();
} }
} }
FileCamera::~FileCamera() FileCamera::~FileCamera()
{ {
if ( capture ) if ( capture )
{ {
Terminate(); Terminate();
} }
} }
void FileCamera::Initialise() void FileCamera::Initialise()
{ {
if ( !path[0] ) if ( !path[0] )
{ {
Error( "No path specified for file image" ); Error( "No path specified for file image" );
exit( -1 ); exit( -1 );
} }
} }
void FileCamera::Terminate() void FileCamera::Terminate()
@ -66,26 +66,26 @@ void FileCamera::Terminate()
int FileCamera::PreCapture() int FileCamera::PreCapture()
{ {
struct stat statbuf; struct stat statbuf;
if ( stat( path, &statbuf ) < 0 ) if ( stat( path, &statbuf ) < 0 )
{ {
Error( "Can't stat %s: %s", path, strerror(errno) ); Error( "Can't stat %s: %s", path, strerror(errno) );
return( -1 ); return( -1 );
} }
while ( (time( 0 ) - statbuf.st_mtime) < 1 ) while ( (time( 0 ) - statbuf.st_mtime) < 1 )
{ {
usleep( 100000 ); usleep( 100000 );
} }
return( 0 ); return( 0 );
} }
int FileCamera::Capture( Image &image ) int FileCamera::Capture( Image &image )
{ {
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
} }
int FileCamera::PostCapture() int FileCamera::PostCapture()
{ {
return( 0 ); return( 0 );
} }

View File

@ -33,19 +33,19 @@
class FileCamera : public Camera class FileCamera : public Camera
{ {
protected: 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 );
~FileCamera(); ~FileCamera();
const char *Path() const { return( path ); } const char *Path() const { return( path ); }
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
}; };
#endif // ZM_FILE_CAMERA_H #endif // ZM_FILE_CAMERA_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
*/ */
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
{ {
m_Detectors = source.m_Detectors; m_Detectors = source.m_Detectors;
} }
@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
*/ */
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source) ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
{ {
m_Detectors = source.m_Detectors; m_Detectors = source.m_Detectors;
return *this; return *this;
} }
ImageAnalyser::~ImageAnalyser() ImageAnalyser::~ImageAnalyser()
{ {
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin(); for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
It != m_Detectors.rend(); It != m_Detectors.rend();
++It) ++It)
delete *It; delete *It;
} }
@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser()
*/ */
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
{ {
Event::StringSet zoneSet; Event::StringSet zoneSet;
int score = 0; int score = 0;
for(DetectorsList::iterator It = m_Detectors.begin(); for(DetectorsList::iterator It = m_Detectors.begin();
It != m_Detectors.end(); It != m_Detectors.end();
++It) ++It)
{
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
if (detect_score)
{ {
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet); score += detect_score;
if (detect_score) noteSetMap[(*It)->getDetectionCause()] = zoneSet;
{ if (det_cause.length())
score += detect_score; det_cause += ", ";
noteSetMap[(*It)->getDetectionCause()] = zoneSet; det_cause += (*It)->getDetectionCause();
if (det_cause.length())
det_cause += ", ";
det_cause += (*It)->getDetectionCause();
}
} }
return score; }
return score;
} }

View File

@ -22,18 +22,18 @@ using namespace std;
//! Class for handling image detection. //! Class for handling image detection.
class ImageAnalyser { class ImageAnalyser {
public: public:
//!Default constructor. //!Default constructor.
ImageAnalyser() {}; ImageAnalyser() {};
//! Destructor. //! Destructor.
~ImageAnalyser(); ~ImageAnalyser();
//! Copy constructor. //! Copy constructor.
ImageAnalyser(const ImageAnalyser& source); ImageAnalyser(const ImageAnalyser& source);
//! Overloaded operator=. //! Overloaded operator=.
ImageAnalyser& operator=(const ImageAnalyser& source); ImageAnalyser& operator=(const ImageAnalyser& source);
private: private:

View File

@ -32,65 +32,65 @@ static int jpeg_err_count = 0;
void zm_jpeg_error_exit( j_common_ptr cinfo ) void zm_jpeg_error_exit( j_common_ptr cinfo )
{ {
static char buffer[JMSG_LENGTH_MAX]; static char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
(zmerr->pub.format_message)( cinfo, buffer ); (zmerr->pub.format_message)( cinfo, buffer );
Error( "%s", buffer ); Error( "%s", buffer );
if ( ++jpeg_err_count == MAX_JPEG_ERRS ) if ( ++jpeg_err_count == MAX_JPEG_ERRS )
{ {
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count ); Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
} }
longjmp( zmerr->setjmp_buffer, 1 ); longjmp( zmerr->setjmp_buffer, 1 );
} }
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level ) void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
{ {
static char buffer[JMSG_LENGTH_MAX]; static char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
if ( msg_level < 0 ) if ( msg_level < 0 )
{ {
/* It's a warning message. Since corrupt files may generate many warnings, /* It's a warning message. Since corrupt files may generate many warnings,
* the policy implemented here is to show only the first warning, * the policy implemented here is to show only the first warning,
* unless trace_level >= 3. * unless trace_level >= 3.
*/ */
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
{ {
(zmerr->pub.format_message)( cinfo, buffer ); (zmerr->pub.format_message)( cinfo, buffer );
if (!strstr(buffer, "Corrupt JPEG data:")) if (!strstr(buffer, "Corrupt JPEG data:"))
Warning( "%s", buffer ); Warning( "%s", buffer );
} }
/* Always count warnings in num_warnings. */ /* Always count warnings in num_warnings. */
zmerr->pub.num_warnings++; zmerr->pub.num_warnings++;
} }
else else
{ {
/* It's a trace message. Show it if trace_level >= msg_level. */ /* It's a trace message. Show it if trace_level >= msg_level. */
if ( zmerr->pub.trace_level >= msg_level ) if ( zmerr->pub.trace_level >= msg_level )
{ {
(zmerr->pub.format_message)( cinfo, buffer ); (zmerr->pub.format_message)( cinfo, buffer );
Debug( msg_level, "%s", buffer ); Debug( msg_level, "%s", buffer );
} }
} }
} }
/* Expanded data destination object for memory */ /* Expanded data destination object for memory */
typedef struct typedef struct
{ {
struct jpeg_destination_mgr pub; /* public fields */ struct jpeg_destination_mgr pub; /* public fields */
JOCTET *outbuffer; /* target buffer */ JOCTET *outbuffer; /* target buffer */
int *outbuffer_size; int *outbuffer_size;
JOCTET *buffer; /* start of buffer */ JOCTET *buffer; /* start of buffer */
} mem_destination_mgr; } mem_destination_mgr;
typedef mem_destination_mgr * mem_dest_ptr; typedef mem_destination_mgr * mem_dest_ptr;
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
/* /*
* Initialize destination --- called by jpeg_start_compress * Initialize destination --- called by jpeg_start_compress
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
static void init_destination (j_compress_ptr cinfo) static void init_destination (j_compress_ptr cinfo)
{ {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
/* Allocate the output buffer --- it will be released when done with image */ /* Allocate the output buffer --- it will be released when done with image */
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
dest->pub.next_output_byte = dest->buffer; dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
*(dest->outbuffer_size) = 0; *(dest->outbuffer_size) = 0;
} }
@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo)
static boolean empty_output_buffer (j_compress_ptr cinfo) static boolean empty_output_buffer (j_compress_ptr cinfo)
{ {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE ); memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE; *(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
dest->pub.next_output_byte = dest->buffer; dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
return( TRUE ); return( TRUE );
} }
/* /*
@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo)
static void term_destination (j_compress_ptr cinfo) static void term_destination (j_compress_ptr cinfo)
{ {
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
if ( datacount > 0 ) if ( datacount > 0 )
{ {
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount ); memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
*(dest->outbuffer_size) += datacount; *(dest->outbuffer_size) += datacount;
} }
} }
@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo)
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size ) void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
{ {
mem_dest_ptr dest; mem_dest_ptr dest;
/* The destination object is made permanent so that multiple JPEG images /* The destination object is made permanent so that multiple JPEG images
* can be written to the same file without re-executing jpeg_stdio_dest. * can be written to the same file without re-executing jpeg_stdio_dest.
* This makes it dangerous to use this manager and a different destination * This makes it dangerous to use this manager and a different destination
* manager serially with the same JPEG object, because their private object * manager serially with the same JPEG object, because their private object
* sizes may be different. Caveat programmer. * sizes may be different. Caveat programmer.
*/ */
if ( cinfo->dest == NULL ) if ( cinfo->dest == NULL )
{ {
/* first time for this JPEG object? */ /* first time for this JPEG object? */
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr)); cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
} }
dest = (mem_dest_ptr) cinfo->dest; dest = (mem_dest_ptr) cinfo->dest;
dest->pub.init_destination = init_destination; dest->pub.init_destination = init_destination;
dest->pub.empty_output_buffer = empty_output_buffer; dest->pub.empty_output_buffer = empty_output_buffer;
dest->pub.term_destination = term_destination; dest->pub.term_destination = term_destination;
dest->outbuffer = outbuffer; dest->outbuffer = outbuffer;
dest->outbuffer_size = outbuffer_size; dest->outbuffer_size = outbuffer_size;
} }
/* Expanded data source object for memory input */ /* Expanded data source object for memory input */
typedef struct typedef struct
{ {
struct jpeg_source_mgr pub; /* public fields */ struct jpeg_source_mgr pub; /* public fields */
JOCTET * inbuffer; /* source stream */ JOCTET * inbuffer; /* source stream */
int inbuffer_size; int inbuffer_size;
int inbuffer_size_hwm; /* High water mark */ int inbuffer_size_hwm; /* High water mark */
JOCTET * buffer; /* start of buffer */ JOCTET * buffer; /* start of buffer */
boolean start_of_data; /* have we gotten any data yet? */ boolean start_of_data; /* have we gotten any data yet? */
} mem_source_mgr; } mem_source_mgr;
typedef mem_source_mgr * mem_src_ptr; typedef mem_source_mgr * mem_src_ptr;
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
/* /*
* Initialize source --- called by jpeg_read_header * Initialize source --- called by jpeg_read_header
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
static void init_source (j_decompress_ptr cinfo) static void init_source (j_decompress_ptr cinfo)
{ {
mem_src_ptr src = (mem_src_ptr) cinfo->src; mem_src_ptr src = (mem_src_ptr) cinfo->src;
/* We reset the empty-input-file flag for each image, /* We reset the empty-input-file flag for each image,
* but we don't clear the input buffer. * but we don't clear the input buffer.
* This is correct behavior for reading a series of images from one source. * This is correct behavior for reading a series of images from one source.
*/ */
src->start_of_data = TRUE; src->start_of_data = TRUE;
src->pub.bytes_in_buffer = 0; src->pub.bytes_in_buffer = 0;
} }
@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo)
static boolean fill_input_buffer (j_decompress_ptr cinfo) static boolean fill_input_buffer (j_decompress_ptr cinfo)
{ {
mem_src_ptr src = (mem_src_ptr) cinfo->src; mem_src_ptr src = (mem_src_ptr) cinfo->src;
size_t nbytes; size_t nbytes;
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size ); memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
nbytes = src->inbuffer_size; nbytes = src->inbuffer_size;
if ( nbytes <= 0 ) if ( nbytes <= 0 )
{ {
if ( src->start_of_data ) /* Treat empty input file as fatal error */ if ( src->start_of_data ) /* Treat empty input file as fatal error */
ERREXIT(cinfo, JERR_INPUT_EMPTY); ERREXIT(cinfo, JERR_INPUT_EMPTY);
WARNMS(cinfo, JWRN_JPEG_EOF); WARNMS(cinfo, JWRN_JPEG_EOF);
/* Insert a fake EOI marker */ /* Insert a fake EOI marker */
src->buffer[0] = (JOCTET) 0xFF; src->buffer[0] = (JOCTET) 0xFF;
src->buffer[1] = (JOCTET) JPEG_EOI; src->buffer[1] = (JOCTET) JPEG_EOI;
nbytes = 2; nbytes = 2;
} }
src->pub.next_input_byte = src->buffer; src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = nbytes; src->pub.bytes_in_buffer = nbytes;
src->start_of_data = FALSE; src->start_of_data = FALSE;
return( TRUE ); return( TRUE );
} }
@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo)
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{ {
mem_src_ptr src = (mem_src_ptr) cinfo->src; mem_src_ptr src = (mem_src_ptr) cinfo->src;
/* Just a dumb implementation for now. Could use fseek() except /* Just a dumb implementation for now. Could use fseek() except
* it doesn't work on pipes. Not clear that being smart is worth * it doesn't work on pipes. Not clear that being smart is worth
* any trouble anyway --- large skips are infrequent. * any trouble anyway --- large skips are infrequent.
*/ */
if ( num_bytes > 0 ) if ( num_bytes > 0 )
{
while ( num_bytes > (long) src->pub.bytes_in_buffer )
{ {
while ( num_bytes > (long) src->pub.bytes_in_buffer ) num_bytes -= (long) src->pub.bytes_in_buffer;
{ (void) fill_input_buffer(cinfo);
num_bytes -= (long) src->pub.bytes_in_buffer; /* note we assume that fill_input_buffer will never return FALSE,
(void) fill_input_buffer(cinfo); * so suspension need not be handled.
/* note we assume that fill_input_buffer will never return FALSE, */
* so suspension need not be handled.
*/
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
} }
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
} }
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
static void term_source (j_decompress_ptr cinfo) static void term_source (j_decompress_ptr cinfo)
{ {
/* no work necessary here */ /* no work necessary here */
} }
@ -354,114 +354,114 @@ static void term_source (j_decompress_ptr cinfo)
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size ) void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
{ {
mem_src_ptr src; mem_src_ptr src;
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
if ( cinfo->src == NULL )
{
/* first time for this JPEG object? */
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
src = (mem_src_ptr) cinfo->src;
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
src->inbuffer_size_hwm = inbuffer_size;
}
else
{
src = (mem_src_ptr) cinfo->src;
if ( src->inbuffer_size_hwm < inbuffer_size )
{
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
src->inbuffer_size_hwm = inbuffer_size;
}
}
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
if ( cinfo->src == NULL )
{
/* first time for this JPEG object? */
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
src = (mem_src_ptr) cinfo->src; src = (mem_src_ptr) cinfo->src;
src->pub.init_source = init_source; src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
src->pub.fill_input_buffer = fill_input_buffer; src->inbuffer_size_hwm = inbuffer_size;
src->pub.skip_input_data = skip_input_data; }
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ else
src->pub.term_source = term_source; {
src->inbuffer = (JOCTET *)inbuffer; src = (mem_src_ptr) cinfo->src;
src->inbuffer_size = inbuffer_size; if ( src->inbuffer_size_hwm < inbuffer_size )
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ {
src->pub.next_input_byte = NULL; /* until buffer loaded */ src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
src->inbuffer_size_hwm = inbuffer_size;
}
}
src = (mem_src_ptr) cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
src->pub.term_source = term_source;
src->inbuffer = (JOCTET *)inbuffer;
src->inbuffer_size = inbuffer_size;
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
} }
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) { void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */ /* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
/* IMPORTANT: these are only valid for 8-bit data precision! */ /* IMPORTANT: these are only valid for 8-bit data precision! */
static const JHUFF_TBL dclumin = { static const JHUFF_TBL dclumin = {
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
FALSE FALSE
}; };
static const JHUFF_TBL dcchrome = { static const JHUFF_TBL dcchrome = {
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
FALSE FALSE
}; };
static const JHUFF_TBL aclumin = { static const JHUFF_TBL aclumin = {
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }, { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa }, 0xf9, 0xfa },
FALSE FALSE
}; };
static const JHUFF_TBL acchrome = { static const JHUFF_TBL acchrome = {
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }, { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa }, 0xf9, 0xfa },
FALSE FALSE
}; };
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin; cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome; cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin; cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome; cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
} }
} }

View File

@ -32,8 +32,8 @@ extern "C"
/* Stuff for overriden error handlers */ /* Stuff for overriden error handlers */
struct zm_error_mgr struct zm_error_mgr
{ {
struct jpeg_error_mgr pub; struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer; jmp_buf setjmp_buffer;
}; };
typedef struct zm_error_mgr *zm_error_ptr; typedef struct zm_error_mgr *zm_error_ptr;

View File

@ -25,102 +25,102 @@
// Do all the buffer checking work here to avoid unnecessary locking // Do all the buffer checking work here to avoid unnecessary locking
void* LibvlcLockBuffer(void* opaque, void** planes) void* LibvlcLockBuffer(void* opaque, void** planes)
{ {
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
data->mutex.lock(); data->mutex.lock();
uint8_t* buffer = data->buffer; uint8_t* buffer = data->buffer;
data->buffer = data->prevBuffer; data->buffer = data->prevBuffer;
data->prevBuffer = buffer; data->prevBuffer = buffer;
*planes = data->buffer; *planes = data->buffer;
return NULL; return NULL;
} }
void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes) void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes)
{ {
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
bool newFrame = false; bool newFrame = false;
for(uint32_t i = 0; i < data->bufferSize; i++) for(uint32_t i = 0; i < data->bufferSize; i++)
{
if(data->buffer[i] != data->prevBuffer[i])
{ {
if(data->buffer[i] != data->prevBuffer[i]) newFrame = true;
{ break;
newFrame = true;
break;
}
}
data->mutex.unlock();
time_t now;
time(&now);
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
if(newFrame || difftime(now, data->prevTime) >= 0.8)
{
data->prevTime = now;
data->newImage.updateValueSignal(true);
} }
}
data->mutex.unlock();
time_t now;
time(&now);
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
if(newFrame || difftime(now, data->prevTime) >= 0.8)
{
data->prevTime = now;
data->newImage.updateValueSignal(true);
}
} }
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
mPath( p_path ), mPath( p_path ),
mMethod( p_method ), mMethod( p_method ),
mOptions( p_options ) mOptions( p_options )
{ {
mLibvlcInstance = NULL; mLibvlcInstance = NULL;
mLibvlcMedia = NULL; mLibvlcMedia = NULL;
mLibvlcMediaPlayer = NULL; mLibvlcMediaPlayer = NULL;
mLibvlcData.buffer = NULL; mLibvlcData.buffer = NULL;
mLibvlcData.prevBuffer = NULL; mLibvlcData.prevBuffer = NULL;
/* 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 */
if(colours == ZM_COLOUR_RGB32) { if(colours == ZM_COLOUR_RGB32) {
subpixelorder = ZM_SUBPIX_ORDER_BGRA; subpixelorder = ZM_SUBPIX_ORDER_BGRA;
mTargetChroma = "RV32"; mTargetChroma = "RV32";
mBpp = 4; mBpp = 4;
} else if(colours == ZM_COLOUR_RGB24) { } else if(colours == ZM_COLOUR_RGB24) {
subpixelorder = ZM_SUBPIX_ORDER_BGR; subpixelorder = ZM_SUBPIX_ORDER_BGR;
mTargetChroma = "RV24"; mTargetChroma = "RV24";
mBpp = 3; mBpp = 3;
} else if(colours == ZM_COLOUR_GRAY8) { } else if(colours == ZM_COLOUR_GRAY8) {
subpixelorder = ZM_SUBPIX_ORDER_NONE; subpixelorder = ZM_SUBPIX_ORDER_NONE;
mTargetChroma = "GREY"; mTargetChroma = "GREY";
mBpp = 1; mBpp = 1;
} else { } else {
Panic("Unexpected colours: %d",colours); Panic("Unexpected colours: %d",colours);
} }
if ( capture ) if ( capture )
{ {
Initialise(); Initialise();
} }
} }
LibvlcCamera::~LibvlcCamera() LibvlcCamera::~LibvlcCamera()
{ {
if ( capture ) if ( capture )
{ {
Terminate(); Terminate();
} }
if(mLibvlcMediaPlayer != NULL) if(mLibvlcMediaPlayer != NULL)
{ {
libvlc_media_player_release(mLibvlcMediaPlayer); libvlc_media_player_release(mLibvlcMediaPlayer);
mLibvlcMediaPlayer = NULL; mLibvlcMediaPlayer = NULL;
} }
if(mLibvlcMedia != NULL) if(mLibvlcMedia != NULL)
{ {
libvlc_media_release(mLibvlcMedia); libvlc_media_release(mLibvlcMedia);
mLibvlcMedia = NULL; mLibvlcMedia = NULL;
} }
if(mLibvlcInstance != NULL) if(mLibvlcInstance != NULL)
{ {
libvlc_release(mLibvlcInstance); libvlc_release(mLibvlcInstance);
mLibvlcInstance = NULL; mLibvlcInstance = NULL;
} }
if (mOptArgV != NULL) if (mOptArgV != NULL)
{ {
delete[] mOptArgV; delete[] mOptArgV;
} }
} }
void LibvlcCamera::Initialise() void LibvlcCamera::Initialise()
@ -129,91 +129,91 @@ void LibvlcCamera::Initialise()
void LibvlcCamera::Terminate() void LibvlcCamera::Terminate()
{ {
libvlc_media_player_stop(mLibvlcMediaPlayer); libvlc_media_player_stop(mLibvlcMediaPlayer);
if(mLibvlcData.buffer != NULL) if(mLibvlcData.buffer != NULL)
{ {
zm_freealigned(mLibvlcData.buffer); zm_freealigned(mLibvlcData.buffer);
} }
if(mLibvlcData.prevBuffer != NULL) if(mLibvlcData.prevBuffer != NULL)
{ {
zm_freealigned(mLibvlcData.prevBuffer); zm_freealigned(mLibvlcData.prevBuffer);
} }
} }
int LibvlcCamera::PrimeCapture() int LibvlcCamera::PrimeCapture()
{ {
Info("Priming capture from %s", mPath.c_str()); Info("Priming capture from %s", mPath.c_str());
StringVector opVect = split(Options(), ","); StringVector opVect = split(Options(), ",");
// Set transport method as specified by method field, rtpUni is default // Set transport method as specified by method field, rtpUni is default
if ( Method() == "rtpMulti" ) if ( Method() == "rtpMulti" )
opVect.push_back("--rtsp-mcast"); opVect.push_back("--rtsp-mcast");
else if ( Method() == "rtpRtsp" ) else if ( Method() == "rtpRtsp" )
opVect.push_back("--rtsp-tcp"); opVect.push_back("--rtsp-tcp");
else if ( Method() == "rtpRtspHttp" ) else if ( Method() == "rtpRtspHttp" )
opVect.push_back("--rtsp-http"); opVect.push_back("--rtsp-http");
if (opVect.size() > 0) if (opVect.size() > 0)
{ {
mOptArgV = new char*[opVect.size()]; mOptArgV = new char*[opVect.size()];
Debug(2, "Number of Options: %d",opVect.size()); Debug(2, "Number of Options: %d",opVect.size());
for (size_t i=0; i< opVect.size(); i++) { for (size_t i=0; i< opVect.size(); i++) {
opVect[i] = trimSpaces(opVect[i]); opVect[i] = trimSpaces(opVect[i]);
mOptArgV[i] = (char *)opVect[i].c_str(); mOptArgV[i] = (char *)opVect[i].c_str();
Debug(2, "set option %d to '%s'", i, opVect[i].c_str()); Debug(2, "set option %d to '%s'", i, opVect[i].c_str());
}
} }
}
mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV); mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV);
if(mLibvlcInstance == NULL) if(mLibvlcInstance == NULL)
Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg()); Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg());
mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str());
if(mLibvlcMedia == NULL) if(mLibvlcMedia == NULL)
Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg());
mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia);
if(mLibvlcMediaPlayer == NULL) if(mLibvlcMediaPlayer == NULL)
Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg());
libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData);
mLibvlcData.bufferSize = width * height * mBpp; mLibvlcData.bufferSize = width * height * mBpp;
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
mLibvlcData.newImage.setValueImmediate(false); mLibvlcData.newImage.setValueImmediate(false);
libvlc_media_player_play(mLibvlcMediaPlayer); libvlc_media_player_play(mLibvlcMediaPlayer);
return(0); return(0);
} }
int LibvlcCamera::PreCapture() int LibvlcCamera::PreCapture()
{ {
return(0); return(0);
} }
// Should not return -1 as cancels capture. Always wait for image if available. // Should not return -1 as cancels capture. Always wait for image if available.
int LibvlcCamera::Capture( Image &image ) int LibvlcCamera::Capture( Image &image )
{ {
while(!mLibvlcData.newImage.getValueImmediate()) while(!mLibvlcData.newImage.getValueImmediate())
mLibvlcData.newImage.getUpdatedValue(1); mLibvlcData.newImage.getUpdatedValue(1);
mLibvlcData.mutex.lock(); mLibvlcData.mutex.lock();
image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp);
mLibvlcData.newImage.setValueImmediate(false); mLibvlcData.newImage.setValueImmediate(false);
mLibvlcData.mutex.unlock(); mLibvlcData.mutex.unlock();
return (0); return (0);
} }
int LibvlcCamera::PostCapture() int LibvlcCamera::PostCapture()
{ {
return(0); return(0);
} }
#endif // HAVE_LIBVLC #endif // HAVE_LIBVLC

View File

@ -33,44 +33,44 @@
// Used by libvlc callbacks // Used by libvlc callbacks
struct LibvlcPrivateData struct LibvlcPrivateData
{ {
uint8_t* buffer; uint8_t* buffer;
uint8_t* prevBuffer; uint8_t* prevBuffer;
time_t prevTime; time_t prevTime;
uint32_t bufferSize; uint32_t bufferSize;
Mutex mutex; Mutex mutex;
ThreadData<bool> newImage; ThreadData<bool> newImage;
}; };
class LibvlcCamera : public Camera class LibvlcCamera : public Camera
{ {
protected: protected:
std::string mPath; std::string mPath;
std::string mMethod; std::string mMethod;
std::string mOptions; std::string mOptions;
char **mOptArgV; char **mOptArgV;
LibvlcPrivateData mLibvlcData; LibvlcPrivateData mLibvlcData;
std::string mTargetChroma; std::string mTargetChroma;
uint8_t mBpp; uint8_t mBpp;
libvlc_instance_t *mLibvlcInstance; libvlc_instance_t *mLibvlcInstance;
libvlc_media_t *mLibvlcMedia; libvlc_media_t *mLibvlcMedia;
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 );
~LibvlcCamera(); ~LibvlcCamera();
const std::string &Path() const { return( mPath ); } const std::string &Path() const { return( mPath ); }
const std::string &Options() const { return( mOptions ); } const std::string &Options() const { return( mOptions ); }
const std::string &Method() const { return( mMethod ); } const std::string &Method() const { return( mMethod ); }
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
}; };
#endif // HAVE_LIBVLC #endif // HAVE_LIBVLC

File diff suppressed because it is too large Load Diff

View File

@ -47,99 +47,99 @@ class LocalCamera : public Camera
{ {
protected: protected:
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
struct V4L2MappedBuffer struct V4L2MappedBuffer
{ {
void *start; void *start;
size_t length; size_t length;
}; };
struct V4L2Data struct V4L2Data
{ {
v4l2_cropcap cropcap; v4l2_cropcap cropcap;
v4l2_crop crop; v4l2_crop crop;
v4l2_format fmt; v4l2_format fmt;
v4l2_requestbuffers reqbufs; v4l2_requestbuffers reqbufs;
V4L2MappedBuffer *buffers; V4L2MappedBuffer *buffers;
v4l2_buffer *bufptr; v4l2_buffer *bufptr;
}; };
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
struct V4L1Data struct V4L1Data
{ {
int active_frame; int active_frame;
video_mbuf frames; video_mbuf frames;
video_mmap *buffers; video_mmap *buffers;
unsigned char *bufptr; unsigned char *bufptr;
}; };
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
protected: protected:
std::string device; std::string device;
int channel; int channel;
int standard; int standard;
int palette; int palette;
bool device_prime; bool device_prime;
bool channel_prime; bool channel_prime;
int channel_index; int channel_index;
unsigned int extras; unsigned int extras;
unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */
convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ convert_fptr_t conversion_fptr; /* Pointer to conversion function used */
uint32_t AutoSelectFormat(int p_colours); uint32_t AutoSelectFormat(int p_colours);
static int camera_count; static int camera_count;
static int channel_count; static int channel_count;
static int channels[VIDEO_MAX_FRAME]; static int channels[VIDEO_MAX_FRAME];
static int standards[VIDEO_MAX_FRAME]; static int standards[VIDEO_MAX_FRAME];
static int vid_fd; static int vid_fd;
static int v4l_version; static int v4l_version;
bool v4l_multi_buffer; bool v4l_multi_buffer;
unsigned int v4l_captures_per_frame; unsigned int v4l_captures_per_frame;
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
static V4L2Data v4l2_data; static V4L2Data v4l2_data;
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
static V4L1Data v4l1_data; static V4L1Data v4l1_data;
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
static AVFrame **capturePictures; static AVFrame **capturePictures;
_AVPIXELFORMAT imagePixFormat; _AVPIXELFORMAT imagePixFormat;
_AVPIXELFORMAT capturePixFormat; _AVPIXELFORMAT capturePixFormat;
struct SwsContext *imgConversionContext; struct SwsContext *imgConversionContext;
AVFrame *tmpPicture; AVFrame *tmpPicture;
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
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, unsigned int p_extras = 0);
~LocalCamera(); ~LocalCamera();
void Initialise(); void Initialise();
void Terminate(); void Terminate();
const std::string &Device() const { return( device ); } const std::string &Device() const { return( device ); }
int Channel() const { return( channel ); } int Channel() const { return( channel ); }
int Standard() const { return( standard ); } int Standard() const { return( standard ); }
int Palette() const { return( palette ); } int Palette() const { return( palette ); }
int Extras() const { return( extras ); } int Extras() const { return( extras ); }
int Brightness( int p_brightness=-1 ); int Brightness( int p_brightness=-1 );
int Hue( int p_hue=-1 ); int Hue( int p_hue=-1 );
int Colour( int p_colour=-1 ); int Colour( int p_colour=-1 );
int Contrast( int p_contrast=-1 ); int Contrast( int p_contrast=-1 );
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
}; };
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L

File diff suppressed because it is too large Load Diff

View File

@ -33,190 +33,190 @@
class Logger class Logger
{ {
public: public:
enum { enum {
NOOPT=-6, NOOPT=-6,
NOLOG, NOLOG,
PANIC, PANIC,
FATAL, FATAL,
ERROR, ERROR,
WARNING, WARNING,
INFO, INFO,
DEBUG1, DEBUG1,
DEBUG2, DEBUG2,
DEBUG3, DEBUG3,
DEBUG4, DEBUG4,
DEBUG5, DEBUG5,
DEBUG6, DEBUG6,
DEBUG7, DEBUG7,
DEBUG8, DEBUG8,
DEBUG9 DEBUG9
}; };
typedef int Level; typedef int Level;
typedef std::map<Level,std::string> StringMap; typedef std::map<Level,std::string> StringMap;
typedef std::map<Level,int> IntMap; typedef std::map<Level,int> IntMap;
class Options class Options
{ {
public: public:
int mTermLevel; int mTermLevel;
int mDatabaseLevel; int mDatabaseLevel;
int mFileLevel; int mFileLevel;
int mSyslogLevel; int mSyslogLevel;
std::string mLogPath;
std::string mLogFile;
public:
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
mTermLevel( termLevel ),
mDatabaseLevel( databaseLevel ),
mFileLevel( fileLevel ),
mSyslogLevel( syslogLevel ),
mLogPath( logPath ),
mLogFile( logFile )
{
}
};
private:
static bool smInitialised;
static Logger *smInstance;
static StringMap smCodes;
static IntMap smSyslogPriorities;
private:
bool mInitialised;
std::string mId;
std::string mIdRoot;
std::string mIdArgs;
Level mLevel; // Level that is currently in operation
Level mTermLevel; // Maximum level output via terminal
Level mDatabaseLevel; // Maximum level output via database
Level mFileLevel; // Maximum level output via file
Level mSyslogLevel; // Maximum level output via syslog
Level mEffectiveLevel; // Level optimised to take account of maxima
bool mDbConnected;
MYSQL mDbConnection;
std::string mLogPath; std::string mLogPath;
std::string mLogFile; std::string mLogFile;
FILE *mLogFileFP;
bool mHasTerm; public:
bool mFlush; Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
mTermLevel( termLevel ),
mDatabaseLevel( databaseLevel ),
mFileLevel( fileLevel ),
mSyslogLevel( syslogLevel ),
mLogPath( logPath ),
mLogFile( logFile )
{
}
};
private: private:
static void usrHandler( int sig ); static bool smInitialised;
static Logger *smInstance;
public: static StringMap smCodes;
friend void logInit( const char *name, const Options &options ); static IntMap smSyslogPriorities;
friend void logTerm();
static Logger *fetch()
{
if ( !smInstance )
{
smInstance = new Logger();
Options options;
smInstance->initialise( "undef", options );
}
return( smInstance );
}
private: private:
Logger(); bool mInitialised;
~Logger();
public: std::string mId;
void initialise( const std::string &id, const Options &options ); std::string mIdRoot;
void terminate(); std::string mIdArgs;
Level mLevel; // Level that is currently in operation
Level mTermLevel; // Maximum level output via terminal
Level mDatabaseLevel; // Maximum level output via database
Level mFileLevel; // Maximum level output via file
Level mSyslogLevel; // Maximum level output via syslog
Level mEffectiveLevel; // Level optimised to take account of maxima
bool mDbConnected;
MYSQL mDbConnection;
std::string mLogPath;
std::string mLogFile;
FILE *mLogFileFP;
bool mHasTerm;
bool mFlush;
private: private:
int limit( int level ) static void usrHandler( int sig );
{
if ( level > DEBUG9 )
return( DEBUG9 );
if ( level < NOLOG )
return( NOLOG );
return( level );
}
bool boolEnv( const std::string &name, bool defaultValue=false );
int intEnv( const std::string &name, bool defaultValue=0 );
std::string strEnv( const std::string &name, const std::string defaultValue="" );
char *getTargettedEnv( const std::string &name );
void loadEnv();
public: public:
const std::string &id() const friend void logInit( const char *name, const Options &options );
friend void logTerm();
static Logger *fetch()
{
if ( !smInstance )
{ {
return( mId ); smInstance = new Logger();
Options options;
smInstance->initialise( "undef", options );
} }
return( smInstance );
const std::string &id( const std::string &id ); }
Level level() const
{
return( mLevel );
}
Level level( Level=NOOPT );
bool debugOn()
{
return( mEffectiveLevel >= DEBUG1 );
}
Level termLevel( Level=NOOPT );
Level databaseLevel( Level=NOOPT );
Level fileLevel( Level=NOOPT );
Level syslogLevel( Level=NOOPT );
private: private:
void logFile( const std::string &logFile ); Logger();
void openFile(); ~Logger();
void closeFile();
void openSyslog();
void closeSyslog();
void closeDatabase();
public: public:
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ); void initialise( const std::string &id, const Options &options );
void terminate();
private:
int limit( int level )
{
if ( level > DEBUG9 )
return( DEBUG9 );
if ( level < NOLOG )
return( NOLOG );
return( level );
}
bool boolEnv( const std::string &name, bool defaultValue=false );
int intEnv( const std::string &name, bool defaultValue=0 );
std::string strEnv( const std::string &name, const std::string defaultValue="" );
char *getTargettedEnv( const std::string &name );
void loadEnv();
public:
const std::string &id() const
{
return( mId );
}
const std::string &id( const std::string &id );
Level level() const
{
return( mLevel );
}
Level level( Level=NOOPT );
bool debugOn()
{
return( mEffectiveLevel >= DEBUG1 );
}
Level termLevel( Level=NOOPT );
Level databaseLevel( Level=NOOPT );
Level fileLevel( Level=NOOPT );
Level syslogLevel( Level=NOOPT );
private:
void logFile( const std::string &logFile );
void openFile();
void closeFile();
void openSyslog();
void closeSyslog();
void closeDatabase();
public:
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
}; };
void logInit( const char *name, const Logger::Options &options=Logger::Options() ); void logInit( const char *name, const Logger::Options &options=Logger::Options() );
void logTerm(); void logTerm();
inline const std::string &logId() inline const std::string &logId()
{ {
return( Logger::fetch()->id() ); return( Logger::fetch()->id() );
} }
inline Logger::Level logLevel() inline Logger::Level logLevel()
{ {
return( Logger::fetch()->level() ); return( Logger::fetch()->level() );
} }
inline void logCapLevel( Logger::Level level ) inline void logCapLevel( Logger::Level level )
{ {
Logger::fetch()->level( level ); Logger::fetch()->level( level );
} }
inline Logger::Level logDebugging() inline Logger::Level logDebugging()
{ {
return( Logger::fetch()->debugOn() ); return( Logger::fetch()->debugOn() );
} }
#define logPrintf(logLevel,params...) {\ #define logPrintf(logLevel,params...) {\
if ( logLevel <= Logger::fetch()->level() )\ if ( logLevel <= Logger::fetch()->level() )\
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\ Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
} }
#define logHexdump(logLevel,data,len) {\ #define logHexdump(logLevel,data,len) {\
if ( logLevel <= Logger::fetch()->level() )\ if ( logLevel <= Logger::fetch()->level() )\
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\ Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
} }
/* Debug compiled out */ /* Debug compiled out */
#ifndef DBG_OFF #ifndef DBG_OFF
@ -228,19 +228,19 @@ inline Logger::Level logDebugging()
#endif #endif
/* Standard debug calls */ /* Standard debug calls */
#define Info(params...) logPrintf(Logger::INFO,##params) #define Info(params...) logPrintf(Logger::INFO,##params)
#define Warning(params...) logPrintf(Logger::WARNING,##params) #define Warning(params...) logPrintf(Logger::WARNING,##params)
#define Error(params...) logPrintf(Logger::ERROR,##params) #define Error(params...) logPrintf(Logger::ERROR,##params)
#define Fatal(params...) logPrintf(Logger::FATAL,##params) #define Fatal(params...) logPrintf(Logger::FATAL,##params)
#define Panic(params...) logPrintf(Logger::PANIC,##params) #define Panic(params...) logPrintf(Logger::PANIC,##params)
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__) #define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
#define Log() Info("Log") #define Log() Info("Log")
#ifdef __GNUC__ #ifdef __GNUC__
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__)) #define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__)) #define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
#else #else
#define Enter(level) #define Enter(level)
#define Exit(level) #define Exit(level)
#endif #endif
#endif // ZM_LOGGER_H #endif // ZM_LOGGER_H

View File

@ -24,138 +24,138 @@
#include "zm.h" #include "zm.h"
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) { inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
uint8_t* retptr; uint8_t* retptr;
#if HAVE_POSIX_MEMALIGN #if HAVE_POSIX_MEMALIGN
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0) if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
return NULL; return NULL;
return retptr; return retptr;
#else #else
uint8_t* alloc; uint8_t* alloc;
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*)); retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
if(retptr == NULL) if(retptr == NULL)
return NULL; return NULL;
alloc = retptr + sizeof(void*); alloc = retptr + sizeof(void*);
if(((long)alloc % reqalignment) != 0) if(((long)alloc % reqalignment) != 0)
alloc = alloc + (reqalignment - ((long)alloc % reqalignment)); alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
/* Store a pointer before to the start of the block, just before returned aligned memory */ /* Store a pointer before to the start of the block, just before returned aligned memory */
*(void**)(alloc - sizeof(void*)) = retptr; *(void**)(alloc - sizeof(void*)) = retptr;
return alloc; return alloc;
#endif #endif
} }
inline void zm_freealigned(void* ptr) { inline void zm_freealigned(void* ptr) {
#if HAVE_POSIX_MEMALIGN #if HAVE_POSIX_MEMALIGN
free(ptr); free(ptr);
#else #else
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */ /* Start of block is stored before the block if it was allocated by zm_mallocaligned */
free(*(void**)((uint8_t*)ptr - sizeof(void*))); free(*(void**)((uint8_t*)ptr - sizeof(void*)));
#endif #endif
} }
inline char *mempbrk( register const char *s, const char *accept, size_t limit ) inline char *mempbrk( register const char *s, const char *accept, size_t limit )
{ {
if ( limit <= 0 || !s || !accept || !*accept ) if ( limit <= 0 || !s || !accept || !*accept )
return( 0 );
register unsigned int i,j;
size_t acc_len = strlen( accept );
for ( i = 0; i < limit; s++, i++ )
{
for ( j = 0; j < acc_len; j++ )
{
if ( *s == accept[j] )
{
return( (char *)s );
}
}
}
return( 0 ); return( 0 );
register unsigned int i,j;
size_t acc_len = strlen( accept );
for ( i = 0; i < limit; s++, i++ )
{
for ( j = 0; j < acc_len; j++ )
{
if ( *s == accept[j] )
{
return( (char *)s );
}
}
}
return( 0 );
} }
inline char *memstr( register const char *s, const char *n, size_t limit ) inline char *memstr( register const char *s, const char *n, size_t limit )
{ {
if ( limit <= 0 || !s || !n ) if ( limit <= 0 || !s || !n )
return( 0 );
if ( !*n )
return( (char *)s );
register unsigned int i,j,k;
size_t n_len = strlen( n );
for ( i = 0; i < limit; i++, s++ )
{
if ( *s != *n )
continue;
j = 1;
k = 1;
while ( true )
{
if ( k >= n_len )
return( (char *)s );
if ( s[j++] != n[k++] )
break;
}
}
return( 0 ); return( 0 );
if ( !*n )
return( (char *)s );
register unsigned int i,j,k;
size_t n_len = strlen( n );
for ( i = 0; i < limit; i++, s++ )
{
if ( *s != *n )
continue;
j = 1;
k = 1;
while ( true )
{
if ( k >= n_len )
return( (char *)s );
if ( s[j++] != n[k++] )
break;
}
}
return( 0 );
} }
inline size_t memspn( register const char *s, const char *accept, size_t limit ) inline size_t memspn( register const char *s, const char *accept, size_t limit )
{ {
if ( limit <= 0 || !s || !accept || !*accept ) if ( limit <= 0 || !s || !accept || !*accept )
return( 0 ); return( 0 );
register unsigned int i,j; register unsigned int i,j;
size_t acc_len = strlen( accept ); size_t acc_len = strlen( accept );
for ( i = 0; i < limit; s++, i++ ) for ( i = 0; i < limit; s++, i++ )
{
register bool found = false;
for ( j = 0; j < acc_len; j++ )
{ {
register bool found = false; if ( *s == accept[j] )
for ( j = 0; j < acc_len; j++ ) {
{ found = true;
if ( *s == accept[j] ) break;
{ }
found = true;
break;
}
}
if ( !found )
{
return( i );
}
} }
return( limit ); if ( !found )
{
return( i );
}
}
return( limit );
} }
inline size_t memcspn( register const char *s, const char *reject, size_t limit ) inline size_t memcspn( register const char *s, const char *reject, size_t limit )
{ {
if ( limit <= 0 || !s || !reject ) if ( limit <= 0 || !s || !reject )
return( 0 ); return( 0 );
if ( !*reject ) if ( !*reject )
return( limit );
register unsigned int i,j;
size_t rej_len = strlen( reject );
for ( i = 0; i < limit; s++, i++ )
{
for ( j = 0; j < rej_len; j++ )
{
if ( *s == reject[j] )
{
return( i );
}
}
}
return( limit ); return( limit );
register unsigned int i,j;
size_t rej_len = strlen( reject );
for ( i = 0; i < limit; s++, i++ )
{
for ( j = 0; j < rej_len; j++ )
{
if ( *s == reject[j] )
{
return( i );
}
}
}
return( limit );
} }
#endif // ZM_MEM_UTILS_H #endif // ZM_MEM_UTILS_H

File diff suppressed because it is too large Load Diff

View File

@ -50,396 +50,396 @@ class Monitor
friend class MonitorStream; friend class MonitorStream;
public: public:
typedef enum typedef enum
{ {
QUERY=0, QUERY=0,
CAPTURE, CAPTURE,
ANALYSIS ANALYSIS
} Purpose; } Purpose;
typedef enum typedef enum
{ {
NONE=1, NONE=1,
MONITOR, MONITOR,
MODECT, MODECT,
RECORD, RECORD,
MOCORD, MOCORD,
NODECT NODECT
} Function; } Function;
typedef enum typedef enum
{ {
ROTATE_0=1, ROTATE_0=1,
ROTATE_90, ROTATE_90,
ROTATE_180, ROTATE_180,
ROTATE_270, ROTATE_270,
FLIP_HORI, FLIP_HORI,
FLIP_VERT FLIP_VERT
} Orientation; } Orientation;
typedef enum typedef enum
{ {
IDLE, IDLE,
PREALARM, PREALARM,
ALARM, ALARM,
ALERT, ALERT,
TAPE TAPE
} 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;
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode; typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
/* 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 */
} SharedData; } SharedData;
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;
uint32_t padding; uint32_t padding;
char trigger_cause[32]; char trigger_cause[32];
char trigger_text[256]; char trigger_text[256];
char trigger_showtext[256]; char trigger_showtext[256];
} 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;
}; };
class MonitorLink class MonitorLink
{ {
protected: protected:
unsigned int id; unsigned int id;
char name[64]; char name[64];
bool connected; bool connected;
time_t last_connect_time; time_t last_connect_time;
#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;
int last_state; int last_state;
int last_event; int last_event;
public: public:
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 );
} }
bool connect(); bool connect();
bool disconnect(); bool disconnect();
bool isAlarmed(); bool isAlarmed();
bool inAlarm(); bool inAlarm();
bool hasAlarmed(); bool hasAlarmed();
}; };
protected: protected:
// These are read from the DB and thereafter remain unchanged // These are read from the DB and thereafter remain unchanged
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 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;
int first_alarm_count; int first_alarm_count;
int last_alarm_count; int last_alarm_count;
int buffer_count; int buffer_count;
int prealarm_count; int prealarm_count;
State state; State state;
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;
Snapshot *image_buffer; Snapshot *image_buffer;
Snapshot next_buffer; /* Used by four field deinterlacing */ Snapshot next_buffer; /* Used by four field deinterlacing */
Snapshot *pre_event_buffer; Snapshot *pre_event_buffer;
Camera *camera; Camera *camera;
Event *event; Event *event;
int n_zones; int n_zones;
Zone **zones; Zone **zones;
struct timeval **timestamps; struct timeval **timestamps;
Image **images; Image **images;
const unsigned char *privacy_bitmask; const unsigned char *privacy_bitmask;
int n_linked_monitors; int n_linked_monitors;
MonitorLink **linked_monitors; MonitorLink **linked_monitors;
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, 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, 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 );
} }
unsigned int Width() const { return( width ); } unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); } unsigned int Height() const { return( height ); }
unsigned int Colours() const { return( camera->Colours() ); } unsigned int Colours() const { return( camera->Colours() ); }
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
State GetState() const; State GetState() const;
int GetImage( int index=-1, int scale=100 ); int GetImage( int index=-1, int scale=100 );
struct timeval GetTimestamp( int index=-1 ) const; struct timeval GetTimestamp( int index=-1 ) const;
void UpdateAdaptiveSkip(); void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate(); useconds_t GetAnalysisRate();
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); } unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
int GetCaptureDelay() const { return( capture_delay ); } int GetCaptureDelay() const { return( capture_delay ); }
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); } int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
unsigned int GetLastReadIndex() const; unsigned int GetLastReadIndex() const;
unsigned int GetLastWriteIndex() const; unsigned int GetLastWriteIndex() const;
unsigned int GetLastEvent() const; unsigned int GetLastEvent() const;
double GetFPS() const; double GetFPS() const;
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
void ForceAlarmOff(); void ForceAlarmOff();
void CancelForced(); void CancelForced();
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); } TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
void actionReload(); void actionReload();
void actionEnable(); void actionEnable();
void actionDisable(); void actionDisable();
void actionSuspend(); void actionSuspend();
void actionResume(); void actionResume();
int actionBrightness( int p_brightness=-1 ); int actionBrightness( int p_brightness=-1 );
int actionHue( int p_hue=-1 ); int actionHue( int p_hue=-1 );
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() );
} }
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
bool CheckSignal( const Image *image ); bool CheckSignal( const Image *image );
bool Analyse(); bool Analyse();
void DumpImage( Image *dump_image ) const; void DumpImage( Image *dump_image ) const;
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
bool closeEvent(); bool closeEvent();
void Reload(); void Reload();
void ReloadZones(); void ReloadZones();
void ReloadLinkedMonitors( const char * ); void ReloadLinkedMonitors( const char * );
bool DumpSettings( char *output, bool verbose ); bool DumpSettings( char *output, bool verbose );
void DumpZoneImage( const char *zone_string=0 ); void DumpZoneImage( const char *zone_string=0 );
#if ZM_HAS_V4L #if ZM_HAS_V4L
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose ); static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ); static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
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 );
void SingleImage( int scale=100 ); void SingleImage( int scale=100 );
void SingleImageRaw( int scale=100 ); void SingleImageRaw( int scale=100 );
void SingleImageZip( int scale=100 ); void SingleImageZip( int scale=100 );
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 ); //void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC
}; };
@ -448,51 +448,51 @@ public:
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 ) void setStreamTTL( time_t p_ttl )
{ {
ttl = p_ttl; ttl = p_ttl;
} }
bool setStreamStart( int monitor_id ) bool setStreamStart( int monitor_id )
{ {
return loadMonitor( monitor_id ); return loadMonitor( monitor_id );
} }
void runStream(); void runStream();
}; };
#endif // ZM_MONITOR_H #endif // ZM_MONITOR_H

File diff suppressed because it is too large Load Diff

View File

@ -27,60 +27,60 @@
class VideoStream class VideoStream
{ {
protected: protected:
struct MimeData struct MimeData
{ {
const char *format; const char *format;
const char *mime_type; const char *mime_type;
}; };
protected: protected:
static bool initialised; static bool initialised;
static struct MimeData mime_data[]; static struct MimeData mime_data[];
protected: protected:
char *codec_and_format; char *codec_and_format;
const char *filename; const char *filename;
const char *format; const char *format;
const char *codec_name; const char *codec_name;
enum _AVPIXELFORMAT pf; enum _AVPIXELFORMAT pf;
AVOutputFormat *of; AVOutputFormat *of;
AVFormatContext *ofc; AVFormatContext *ofc;
AVStream *ost; AVStream *ost;
AVCodec *codec; AVCodec *codec;
AVFrame *opicture; AVFrame *opicture;
AVFrame *tmp_opicture; AVFrame *tmp_opicture;
uint8_t *video_outbuf; uint8_t *video_outbuf;
int video_outbuf_size; int video_outbuf_size;
double last_pts; double last_pts;
pthread_t streaming_thread; pthread_t streaming_thread;
bool do_streaming; bool do_streaming;
uint8_t *buffer_copy; uint8_t *buffer_copy;
bool add_timestamp; bool add_timestamp;
unsigned int timestamp; unsigned int timestamp;
pthread_mutex_t *buffer_copy_lock; pthread_mutex_t *buffer_copy_lock;
int buffer_copy_size; int buffer_copy_size;
int buffer_copy_used; int buffer_copy_used;
AVPacket** packet_buffers; AVPacket** packet_buffers;
int packet_index; int packet_index;
int SendPacket(AVPacket *packet); int SendPacket(AVPacket *packet);
static void* StreamingThreadCallback(void *ctx); static void* StreamingThreadCallback(void *ctx);
protected: protected:
static void Initialise(); static void Initialise();
void SetupFormat( ); void SetupFormat( );
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ); void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
void SetParameters(); void SetParameters();
void ActuallyOpenStream(); void ActuallyOpenStream();
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
public: public:
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ); VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
~VideoStream(); ~VideoStream();
const char *MimeType() const; const char *MimeType() const;
void OpenStream(); void OpenStream();
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
}; };
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC

View File

@ -28,95 +28,95 @@
void Polygon::calcArea() void Polygon::calcArea()
{ {
double float_area = 0.0L; double float_area = 0.0L;
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
{ {
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
float_area += trap_area; float_area += trap_area;
//printf( "%.2f (%.2f)\n", float_area, trap_area ); //printf( "%.2f (%.2f)\n", float_area, trap_area );
} }
area = (int)round(fabs(float_area)); area = (int)round(fabs(float_area));
} }
void Polygon::calcCentre() void Polygon::calcCentre()
{ {
if ( !area && n_coords ) if ( !area && n_coords )
calcArea(); calcArea();
double float_x = 0.0L, float_y = 0.0L; double float_x = 0.0L, float_y = 0.0L;
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
{ {
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
} }
float_x /= (6*area); float_x /= (6*area);
float_y /= (6*area); float_y /= (6*area);
//printf( "%.2f,%.2f\n", float_x, float_y ); //printf( "%.2f,%.2f\n", float_x, float_y );
centre = Coord( (int)round(float_x), (int)round(float_y) ); centre = Coord( (int)round(float_x), (int)round(float_y) );
} }
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords ) Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
{ {
coords = new Coord[n_coords]; coords = new Coord[n_coords];
int min_x = -1; int min_x = -1;
int max_x = -1; int max_x = -1;
int min_y = -1; int min_y = -1;
int max_y = -1; int max_y = -1;
for( int i = 0; i < n_coords; i++ ) for( int i = 0; i < n_coords; i++ )
{ {
coords[i] = p_coords[i]; coords[i] = p_coords[i];
if ( min_x == -1 || coords[i].X() < min_x ) if ( min_x == -1 || coords[i].X() < min_x )
min_x = coords[i].X(); min_x = coords[i].X();
if ( max_x == -1 || coords[i].X() > max_x ) if ( max_x == -1 || coords[i].X() > max_x )
max_x = coords[i].X(); max_x = coords[i].X();
if ( min_y == -1 || coords[i].Y() < min_y ) if ( min_y == -1 || coords[i].Y() < min_y )
min_y = coords[i].Y(); min_y = coords[i].Y();
if ( max_y == -1 || coords[i].Y() > max_y ) if ( max_y == -1 || coords[i].Y() > max_y )
max_y = coords[i].Y(); max_y = coords[i].Y();
} }
extent = Box( min_x, min_y, max_x, max_y ); extent = Box( min_x, min_y, max_x, max_y );
calcArea(); calcArea();
calcCentre(); calcCentre();
} }
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre ) Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
{ {
coords = new Coord[n_coords]; coords = new Coord[n_coords];
for( int i = 0; i < n_coords; i++ ) for( int i = 0; i < n_coords; i++ )
{ {
coords[i] = p_polygon.coords[i]; coords[i] = p_polygon.coords[i];
} }
} }
Polygon &Polygon::operator=( const Polygon &p_polygon ) Polygon &Polygon::operator=( const Polygon &p_polygon )
{ {
if ( n_coords < p_polygon.n_coords ) if ( n_coords < p_polygon.n_coords )
{ {
delete[] coords; delete[] coords;
coords = new Coord[p_polygon.n_coords]; coords = new Coord[p_polygon.n_coords];
} }
n_coords = p_polygon.n_coords; n_coords = p_polygon.n_coords;
for( int i = 0; i < n_coords; i++ ) for( int i = 0; i < n_coords; i++ )
{ {
coords[i] = p_polygon.coords[i]; coords[i] = p_polygon.coords[i];
} }
extent = p_polygon.extent; extent = p_polygon.extent;
area = p_polygon.area; area = p_polygon.area;
centre = p_polygon.centre; centre = p_polygon.centre;
return( *this ); return( *this );
} }
bool Polygon::isInside( const Coord &coord ) const bool Polygon::isInside( const Coord &coord ) const
{ {
bool inside = false; bool inside = false;
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
{ {
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
{ {
inside = !inside; inside = !inside;
} }
} }
return( inside ); return( inside );
} }

View File

@ -33,93 +33,93 @@
class Polygon class Polygon
{ {
protected: protected:
struct Edge struct Edge
{ {
int min_y; int min_y;
int max_y; int max_y;
double min_x; double min_x;
double _1_m; double _1_m;
static int CompareYX( const void *p1, const void *p2 ) static int CompareYX( const void *p1, const void *p2 )
{ {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
if ( e1->min_y == e2->min_y ) if ( e1->min_y == e2->min_y )
return( int(e1->min_x - e2->min_x) ); return( int(e1->min_x - e2->min_x) );
else else
return( int(e1->min_y - e2->min_y) ); return( int(e1->min_y - e2->min_y) );
} }
static int CompareX( const void *p1, const void *p2 ) static int CompareX( const void *p1, const void *p2 )
{ {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
return( int(e1->min_x - e2->min_x) ); return( int(e1->min_x - e2->min_x) );
} }
}; };
struct Slice struct Slice
{ {
int min_x; int min_x;
int max_x; int max_x;
int n_edges; int n_edges;
int *edges; int *edges;
Slice() Slice()
{ {
n_edges = 0; n_edges = 0;
edges = 0; edges = 0;
} }
~Slice() ~Slice()
{ {
delete edges; delete edges;
} }
}; };
protected: protected:
int n_coords; int n_coords;
Coord *coords; Coord *coords;
Box extent; Box extent;
int area; int area;
Coord centre; Coord centre;
Edge *edges; Edge *edges;
Slice *slices; Slice *slices;
protected: protected:
void initialiseEdges(); void initialiseEdges();
void calcArea(); void calcArea();
void calcCentre(); void calcCentre();
public: public:
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ) inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
{ {
} }
Polygon( int p_n_coords, const Coord *p_coords ); Polygon( int p_n_coords, const Coord *p_coords );
Polygon( const Polygon &p_polygon ); Polygon( const Polygon &p_polygon );
~Polygon() ~Polygon()
{ {
delete[] coords; delete[] coords;
} }
Polygon &operator=( const Polygon &p_polygon ); Polygon &operator=( const Polygon &p_polygon );
inline int getNumCoords() const { return( n_coords ); } inline int getNumCoords() const { return( n_coords ); }
inline const Coord &getCoord( int index ) const inline const Coord &getCoord( int index ) const
{ {
return( coords[index] ); return( coords[index] );
} }
inline const Box &Extent() const { return( extent ); } inline const Box &Extent() const { return( extent ); }
inline int LoX() const { return( extent.LoX() ); } inline int LoX() const { return( extent.LoX() ); }
inline int HiX() const { return( extent.HiX() ); } inline int HiX() const { return( extent.HiX() ); }
inline int LoY() const { return( extent.LoY() ); } inline int LoY() const { return( extent.LoY() ); }
inline int HiY() const { return( extent.HiY() ); } inline int HiY() const { return( extent.HiY() ); }
inline int Width() const { return( extent.Width() ); } inline int Width() const { return( extent.Width() ); }
inline int Height() const { return( extent.Height() ); } inline int Height() const { return( extent.Height() ); }
inline int Area() const { return( area ); } inline int Area() const { return( area ); }
inline const Coord &Centre() const inline const Coord &Centre() const
{ {
return( centre ); return( centre );
} }
bool isInside( const Coord &coord ) const; bool isInside( const Coord &coord ) const;
}; };
#endif // ZM_POLY_H #endif // ZM_POLY_H

View File

@ -26,99 +26,99 @@
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 ) RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
{ {
const char *errstr; const char *errstr;
int erroffset = 0; int erroffset = 0;
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) ) if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
{ {
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset ); Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
} }
regextra = pcre_study( regex, 0, &errstr ); regextra = pcre_study( regex, 0, &errstr );
if ( errstr ) if ( errstr )
{ {
Panic( "pcre_study(%s): %s", pattern, errstr ); Panic( "pcre_study(%s): %s", pattern, errstr );
} }
if ( (ok = (bool)regex) ) if ( (ok = (bool)regex) )
{ {
match_vectors = new int[3*max_matches]; match_vectors = new int[3*max_matches];
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches ); memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
match_buffers = new char *[max_matches]; match_buffers = new char *[max_matches];
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches ); memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
match_lengths = new int[max_matches]; match_lengths = new int[max_matches];
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches ); memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
match_valid = new bool[max_matches]; match_valid = new bool[max_matches];
memset( match_valid, 0, sizeof(*match_valid)*max_matches ); memset( match_valid, 0, sizeof(*match_valid)*max_matches );
} }
n_matches = 0; n_matches = 0;
} }
RegExpr::~RegExpr() RegExpr::~RegExpr()
{ {
for ( int i = 0; i < max_matches; i++ ) for ( int i = 0; i < max_matches; i++ )
{ {
if ( match_buffers[i] ) if ( match_buffers[i] )
{ {
delete[] match_buffers[i]; delete[] match_buffers[i];
} }
} }
delete[] match_valid; delete[] match_valid;
delete[] match_lengths; delete[] match_lengths;
delete[] match_buffers; delete[] match_buffers;
delete[] match_vectors; delete[] match_vectors;
} }
int RegExpr::Match( const char *subject_string, int subject_length, int flags ) int RegExpr::Match( const char *subject_string, int subject_length, int flags )
{ {
match_string = subject_string; match_string = subject_string;
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches ); n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
if ( n_matches <= 0 ) if ( n_matches <= 0 )
{ {
if ( n_matches < PCRE_ERROR_NOMATCH ) if ( n_matches < PCRE_ERROR_NOMATCH )
{ {
Error( "Error %d executing regular expression", n_matches ); Error( "Error %d executing regular expression", n_matches );
} }
return( n_matches = 0 ); return( n_matches = 0 );
} }
for( int i = 0; i < max_matches; i++ ) for( int i = 0; i < max_matches; i++ )
{ {
match_valid[i] = false; match_valid[i] = false;
} }
return( n_matches ); return( n_matches );
} }
const char *RegExpr::MatchString( int match_index ) const const char *RegExpr::MatchString( int match_index ) const
{ {
if ( match_index > n_matches ) if ( match_index > n_matches )
{ {
return( 0 ); return( 0 );
} }
if ( !match_valid[match_index] ) if ( !match_valid[match_index] )
{ {
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index]; int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
if ( match_lengths[match_index] < (match_len+1) ) if ( match_lengths[match_index] < (match_len+1) )
{ {
delete[] match_buffers[match_index]; delete[] match_buffers[match_index];
match_buffers[match_index] = new char[match_len+1]; match_buffers[match_index] = new char[match_len+1];
match_lengths[match_index] = match_len+1; match_lengths[match_index] = match_len+1;
} }
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len ); memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
match_buffers[match_index][match_len] = '\0'; match_buffers[match_index][match_len] = '\0';
match_valid[match_index] = true; match_valid[match_index] = true;
} }
return( match_buffers[match_index] ); return( match_buffers[match_index] );
} }
int RegExpr::MatchLength( int match_index ) const int RegExpr::MatchLength( int match_index ) const
{ {
if ( match_index > n_matches ) if ( match_index > n_matches )
{ {
return( 0 ); return( 0 );
} }
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] ); return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
} }
#endif // HAVE_LIBPCRE #endif // HAVE_LIBPCRE

View File

@ -35,29 +35,29 @@
class RegExpr class RegExpr
{ {
protected: protected:
pcre *regex; pcre *regex;
pcre_extra *regextra; pcre_extra *regextra;
int max_matches; int max_matches;
int *match_vectors; int *match_vectors;
mutable char **match_buffers; mutable char **match_buffers;
int *match_lengths; int *match_lengths;
bool *match_valid; bool *match_valid;
protected: protected:
const char *match_string; const char *match_string;
int n_matches; int n_matches;
protected: protected:
bool ok; bool ok;
public: public:
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 ); RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
~RegExpr(); ~RegExpr();
bool Ok() const { return( ok ); } bool Ok() const { return( ok ); }
int MatchCount() const { return( n_matches ); } int MatchCount() const { return( n_matches ); }
int Match( const char *subject_string, int subject_length, int flags=0 ); int Match( const char *subject_string, int subject_length, int flags=0 );
const char *MatchString( int match_index ) const; const char *MatchString( int match_index ) const;
int MatchLength( int match_index ) const; int MatchLength( int match_index ) const;
}; };
#endif // HAVE_LIBPCRE #endif // HAVE_LIBPCRE

View File

@ -22,66 +22,66 @@
#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( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
protocol( p_protocol ), protocol( p_protocol ),
host( p_host ), host( p_host ),
port( p_port ), port( p_port ),
path( p_path ), path( p_path ),
hp( 0 ) hp( 0 )
{ {
if ( path[0] != '/' ) if ( path[0] != '/' )
path = '/'+path; path = '/'+path;
} }
RemoteCamera::~RemoteCamera() RemoteCamera::~RemoteCamera()
{ {
if(hp != NULL) { if(hp != NULL) {
freeaddrinfo(hp); freeaddrinfo(hp);
hp = NULL; hp = NULL;
} }
} }
void RemoteCamera::Initialise() void RemoteCamera::Initialise()
{ {
if( protocol.empty() ) if( protocol.empty() )
Fatal( "No protocol specified for remote camera" ); Fatal( "No protocol specified for remote camera" );
if( host.empty() ) if( host.empty() )
Fatal( "No host specified for remote camera" ); Fatal( "No host specified for remote camera" );
if( port.empty() ) if( port.empty() )
Fatal( "No port specified for remote camera" ); Fatal( "No port specified for remote camera" );
//if( path.empty() ) //if( path.empty() )
//Fatal( "No path specified for remote camera" ); //Fatal( "No path specified for remote camera" );
// Cache as much as we can to speed things up // Cache as much as we can to speed things up
std::string::size_type authIndex = host.rfind( '@' ); std::string::size_type authIndex = host.rfind( '@' );
if ( authIndex != std::string::npos ) if ( authIndex != std::string::npos )
{ {
auth = host.substr( 0, authIndex ); auth = host.substr( 0, authIndex );
host.erase( 0, authIndex+1 ); host.erase( 0, authIndex+1 );
auth64 = base64Encode( auth ); auth64 = base64Encode( auth );
authIndex = auth.rfind( ':' ); authIndex = auth.rfind( ':' );
username = auth.substr(0,authIndex); username = auth.substr(0,authIndex);
password = auth.substr( authIndex+1, auth.length() ); password = auth.substr( authIndex+1, auth.length() );
} }
mNeedAuth = false; mNeedAuth = false;
mAuthenticator = new zm::Authenticator(username,password); mAuthenticator = new zm::Authenticator(username,password);
struct addrinfo hints; struct addrinfo hints;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
if ( ret != 0 ) if ( ret != 0 )
{ {
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
} }
} }

View File

@ -35,44 +35,44 @@
class RemoteCamera : public Camera class RemoteCamera : public Camera
{ {
protected: protected:
std::string protocol; std::string protocol;
std::string host; std::string host;
std::string port; std::string port;
std::string path; std::string path;
std::string auth; std::string auth;
std::string username; std::string username;
std::string password; std::string password;
std::string auth64; std::string auth64;
// Reworked authentication system // Reworked authentication system
// First try without authentication, even if we have a username and password // First try without authentication, even if we have a username and password
// on receiving a 401 response, select authentication method (basic or digest) // on receiving a 401 response, select authentication method (basic or digest)
// fill required fields and set needAuth // fill required fields and set needAuth
// subsequent requests can set the required authentication header. // subsequent requests can set the required authentication header.
bool mNeedAuth; bool mNeedAuth;
zm::Authenticator* mAuthenticator; zm::Authenticator* mAuthenticator;
protected: 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 );
virtual ~RemoteCamera(); virtual ~RemoteCamera();
const std::string &Protocol() const { return( protocol ); } const std::string &Protocol() const { return( protocol ); }
const std::string &Host() const { return( host ); } const std::string &Host() const { return( host ); }
const std::string &Port() const { return( port ); } const std::string &Port() const { return( port ); }
const std::string &Path() const { return( path ); } const std::string &Path() const { return( path ); }
const std::string &Auth() const { return( auth ); } const std::string &Auth() const { return( auth ); }
const std::string &Username() const { return( username ); } const std::string &Username() const { return( username ); }
const std::string &Password() const { return( password ); } const std::string &Password() const { return( password ); }
virtual void Initialise(); virtual void Initialise();
virtual void Terminate() = 0; virtual void Terminate() = 0;
virtual int Connect() = 0; virtual int Connect() = 0;
virtual int Disconnect() = 0; virtual int Disconnect() = 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;
}; };
#endif // ZM_REMOTE_CAMERA_H #endif // ZM_REMOTE_CAMERA_H

File diff suppressed because it is too large Load Diff

View File

@ -33,31 +33,31 @@
class RemoteCameraHttp : public RemoteCamera class RemoteCameraHttp : public RemoteCamera
{ {
protected: protected:
std::string request; std::string request;
struct timeval timeout; struct timeval timeout;
//struct hostent *hp; //struct hostent *hp;
//struct sockaddr_in sa; //struct sockaddr_in sa;
int sd; int sd;
Buffer buffer; Buffer buffer;
enum { SINGLE_IMAGE, MULTI_IMAGE } mode; enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
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 );
~RemoteCameraHttp(); ~RemoteCameraHttp();
void Initialise(); void Initialise();
void Terminate() { Disconnect(); } void Terminate() { Disconnect(); }
int Connect(); int Connect();
int Disconnect(); int Disconnect();
int SendRequest(); int SendRequest();
int ReadData( Buffer &buffer, int bytes_expected=0 ); int ReadData( Buffer &buffer, int bytes_expected=0 );
int GetResponse(); int GetResponse();
int PreCapture(); int PreCapture();
int Capture( Image &image ); int Capture( Image &image );
int PostCapture(); int PostCapture();
}; };
#endif // ZM_REMOTE_CAMERA_HTTP_H #endif // ZM_REMOTE_CAMERA_HTTP_H

View File

@ -29,343 +29,343 @@
#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 ) :
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 ),
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 )
{ {
Initialise(); Initialise();
} }
mFormatContext = NULL; mFormatContext = NULL;
mVideoStreamId = -1; mVideoStreamId = -1;
mCodecContext = NULL; mCodecContext = NULL;
mCodec = NULL; mCodec = NULL;
mRawFrame = NULL; mRawFrame = NULL;
mFrame = NULL; mFrame = NULL;
frameCount = 0; frameCount = 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 */
if(colours == ZM_COLOUR_RGB32) { if(colours == ZM_COLOUR_RGB32) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA; subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA; imagePixFormat = AV_PIX_FMT_RGBA;
} else if(colours == ZM_COLOUR_RGB24) { } else if(colours == ZM_COLOUR_RGB24) {
subpixelorder = ZM_SUBPIX_ORDER_RGB; subpixelorder = ZM_SUBPIX_ORDER_RGB;
imagePixFormat = AV_PIX_FMT_RGB24; imagePixFormat = AV_PIX_FMT_RGB24;
} else if(colours == ZM_COLOUR_GRAY8) { } else if(colours == ZM_COLOUR_GRAY8) {
subpixelorder = ZM_SUBPIX_ORDER_NONE; subpixelorder = ZM_SUBPIX_ORDER_NONE;
imagePixFormat = AV_PIX_FMT_GRAY8; imagePixFormat = AV_PIX_FMT_GRAY8;
} else { } else {
Panic("Unexpected colours: %d",colours); Panic("Unexpected colours: %d",colours);
} }
} }
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 )
{ {
Terminate(); Terminate();
} }
} }
void RemoteCameraRtsp::Initialise() void RemoteCameraRtsp::Initialise()
{ {
RemoteCamera::Initialise(); RemoteCamera::Initialise();
int max_size = width*height*colours; int max_size = width*height*colours;
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++ ) 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)
Fatal( "Unable to allocate frame(s)"); Fatal( "Unable to allocate frame(s)");
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size( imagePixFormat, width, height );
if( (unsigned int)pSize != imagesize) { if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
} }
/* /*
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
} }
if(!sws_isSupportedOutput(imagePixFormat)) { if(!sws_isSupportedOutput(imagePixFormat)) {
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
} }
#else // HAVE_LIBSWSCALE #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;
/* 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);
} }
while ( true ) while ( true )
{
buffer.clear();
if ( !rtspThread->isRunning() )
return (-1);
if ( rtspThread->getFrame( buffer ) )
{ {
buffer.clear(); Debug( 3, "Read frame %d bytes", buffer.size() );
if ( !rtspThread->isRunning() ) Debug( 4, "Address %p", buffer.head() );
return (-1); Hexdump( 4, buffer.head(), 16 );
if ( rtspThread->getFrame( buffer ) ) 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)
{ {
Debug( 3, "Read frame %d bytes", buffer.size() ); lastSps = buffer;
Debug( 4, "Address %p", buffer.head() ); continue;
Hexdump( 4, buffer.head(), 16 ); }
// PPS
else if(nalType == 8)
{
lastPps = buffer;
continue;
}
// IDR
else if(nalType == 5)
{
buffer += lastSps;
buffer += lastPps;
}
}
if ( !buffer.size() ) av_init_packet( &packet );
return( -1 );
while ( !frameComplete && buffer.size() > 0 )
if(mCodecContext->codec_id == AV_CODEC_ID_H264) {
{ packet.data = buffer.head();
// SPS and PPS frames should be saved and appended to IDR frames packet.size = buffer.size();
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;
}
}
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) #if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
#else #else
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
#endif #endif
if ( len < 0 ) if ( len < 0 )
{ {
Error( "Error while decoding frame %d", frameCount ); Error( "Error while decoding frame %d", frameCount );
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
buffer.clear(); buffer.clear();
continue; continue;
} }
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
//if ( buffer.size() < 400 ) //if ( buffer.size() < 400 )
//Hexdump( 0, buffer.head(), buffer.size() ); //Hexdump( 0, buffer.head(), buffer.size() );
buffer -= len; buffer -= len;
}
if ( frameComplete ) {
Debug( 3, "Got frame %d", frameCount );
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
#if HAVE_LIBSWSCALE
if(mConvertContext == NULL) {
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
if(mConvertContext == NULL)
Fatal( "Unable to create conversion context");
}
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
#else // HAVE_LIBSWSCALE
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
#endif // HAVE_LIBSWSCALE
frameCount++;
} /* frame complete */
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
av_packet_unref( &packet);
#else
av_free_packet( &packet );
#endif
} /* getFrame() */
if(frameComplete)
return (0);
} }
return (0) ; if ( frameComplete ) {
Debug( 3, "Got frame %d", frameCount );
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
#if HAVE_LIBSWSCALE
if(mConvertContext == NULL) {
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
if(mConvertContext == NULL)
Fatal( "Unable to create conversion context");
}
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
#else // HAVE_LIBSWSCALE
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
#endif // HAVE_LIBSWSCALE
frameCount++;
} /* frame complete */
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
av_packet_unref( &packet);
#else
av_free_packet( &packet );
#endif
} /* getFrame() */
if(frameComplete)
return (0);
}
return (0) ;
} }
int RemoteCameraRtsp::PostCapture() int RemoteCameraRtsp::PostCapture()
{ {
return( 0 ); return( 0 );
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -35,50 +35,50 @@
class RemoteCameraRtsp : public RemoteCamera class RemoteCameraRtsp : public RemoteCamera
{ {
protected: protected:
struct sockaddr_in rtsp_sa; struct sockaddr_in rtsp_sa;
struct sockaddr_in rtcp_sa; struct sockaddr_in rtcp_sa;
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;
RtspThread *rtspThread; RtspThread *rtspThread;
int frameCount; int frameCount;
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
AVFormatContext *mFormatContext; AVFormatContext *mFormatContext;
int mVideoStreamId; int mVideoStreamId;
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
#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 );
~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();
}; };

View File

@ -20,80 +20,80 @@
#ifndef ZM_RGB_H #ifndef ZM_RGB_H
#define ZM_RGB_H #define ZM_RGB_H
typedef uint32_t Rgb; // RGB colour type typedef uint32_t Rgb; // RGB colour type
#define WHITE 0xff #define WHITE 0xff
#define WHITE_R 0xff #define WHITE_R 0xff
#define WHITE_G 0xff #define WHITE_G 0xff
#define WHITE_B 0xff #define WHITE_B 0xff
#define BLACK 0x00 #define BLACK 0x00
#define BLACK_R 0x00 #define BLACK_R 0x00
#define BLACK_G 0x00 #define BLACK_G 0x00
#define BLACK_B 0x00 #define BLACK_B 0x00
#define RGB_WHITE (0x00ffffff) #define RGB_WHITE (0x00ffffff)
#define RGB_BLACK (0x00000000) #define RGB_BLACK (0x00000000)
#define RGB_RED (0x000000ff) #define RGB_RED (0x000000ff)
#define RGB_GREEN (0x0000ff00) #define RGB_GREEN (0x0000ff00)
#define RGB_BLUE (0x00ff0000) #define RGB_BLUE (0x00ff0000)
#define RGB_ORANGE (0x0000a5ff) #define RGB_ORANGE (0x0000a5ff)
#define RGB_PURPLE (0x00800080) #define RGB_PURPLE (0x00800080)
#define RGB_TRANSPARENT (0x01000000) #define RGB_TRANSPARENT (0x01000000)
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff) #define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
/* RGB or RGBA macros */ /* RGB or RGBA macros */
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff) #define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff) #define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
#define RED_VAL_RGBA(v) ((v)&0xff) #define RED_VAL_RGBA(v) ((v)&0xff)
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff) #define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr)) #define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1)) #define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2)) #define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3)) #define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
/* BGR or BGRA */ /* BGR or BGRA */
#define RED_VAL_BGRA(v) (((v)>>16)&0xff) #define RED_VAL_BGRA(v) (((v)>>16)&0xff)
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff) #define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
#define BLUE_VAL_BGRA(v) ((v)&0xff) #define BLUE_VAL_BGRA(v) ((v)&0xff)
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff) #define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2)) #define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1)) #define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr)) #define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3)) #define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
/* ARGB */ /* ARGB */
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff) #define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff) #define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
#define RED_VAL_ARGB(v) (((v)>>8)&0xff) #define RED_VAL_ARGB(v) (((v)>>8)&0xff)
#define ALPHA_VAL_ARGB(v) ((v)&0xff) #define ALPHA_VAL_ARGB(v) ((v)&0xff)
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1)) #define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2)) #define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3)) #define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr)) #define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
/* ABGR */ /* ABGR */
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff) #define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff) #define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
#define RED_VAL_ABGR(v) (((v)>>24)&0xff) #define RED_VAL_ABGR(v) (((v)>>24)&0xff)
#define ALPHA_VAL_ABGR(v) ((v)&0xff) #define ALPHA_VAL_ABGR(v) ((v)&0xff)
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3)) #define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2)) #define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1)) #define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr)) #define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff) #define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00) #define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */ /* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */ /* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */ /* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) // #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3) // #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) // #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3) // #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
/* ZM colours */ /* ZM colours */
#define ZM_COLOUR_RGB32 4 #define ZM_COLOUR_RGB32 4
@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type
/* A macro to use default subpixel order for a specified colour. */ /* A macro to use default subpixel order for a specified colour. */
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */ /* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1) #define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
/* Convert RGB colour value into BGR\ARGB\ABGR */ /* Convert RGB colour value into BGR\ARGB\ABGR */
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) { inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
Rgb result; Rgb result;
switch(p_subpixorder) { switch(p_subpixorder) {
case ZM_SUBPIX_ORDER_BGR: case ZM_SUBPIX_ORDER_BGR:
case ZM_SUBPIX_ORDER_BGRA: case ZM_SUBPIX_ORDER_BGRA:
{ {
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col); BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col); GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col); RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
} }
break; break;
case ZM_SUBPIX_ORDER_ARGB: case ZM_SUBPIX_ORDER_ARGB:
{ {
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col); BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col); GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col); RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
} }
break; break;
case ZM_SUBPIX_ORDER_ABGR: case ZM_SUBPIX_ORDER_ABGR:
{ {
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col); BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col); GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col); RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
} }
break; break;
/* Grayscale */ /* Grayscale */
case ZM_SUBPIX_ORDER_NONE: case ZM_SUBPIX_ORDER_NONE:
result = p_col & 0xff; result = p_col & 0xff;
break; break;
default: default:
return p_col; return p_col;
break; break;
} }
return result; return result;
} }
#endif // ZM_RGB_H #endif // ZM_RGB_H

View File

@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
{ {
const RtcpPacket *rtcpPacket; const RtcpPacket *rtcpPacket;
rtcpPacket = (RtcpPacket *)packet; rtcpPacket = (RtcpPacket *)packet;
int consumed = 0; int consumed = 0;
//printf( "C: " ); //printf( "C: " );
//for ( int i = 0; i < packetLen; i++ ) //for ( int i = 0; i < packetLen; i++ )
//printf( "%02x ", (unsigned char)packet[i] ); //printf( "%02x ", (unsigned char)packet[i] );
//printf( "\n" ); //printf( "\n" );
int ver = rtcpPacket->header.version; int ver = rtcpPacket->header.version;
int count = rtcpPacket->header.count; int count = rtcpPacket->header.count;
int pt = rtcpPacket->header.pt; int pt = rtcpPacket->header.pt;
int len = ntohs(rtcpPacket->header.lenN); int len = ntohs(rtcpPacket->header.lenN);
Debug( 5, "RTCP Ver: %d", ver ); Debug( 5, "RTCP Ver: %d", ver );
Debug( 5, "RTCP Count: %d", count ); Debug( 5, "RTCP Count: %d", count );
Debug( 5, "RTCP Pt: %d", pt ); Debug( 5, "RTCP Pt: %d", pt );
Debug( 5, "RTCP len: %d", len ); Debug( 5, "RTCP len: %d", len );
switch( pt ) switch( pt )
{
case RTCP_SR :
{ {
case RTCP_SR : uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
{
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
Debug( 5, "RTCP Got SR (%x)", ssrc ); Debug( 5, "RTCP Got SR (%x)", ssrc );
if ( mRtpSource.getSsrc() ) if ( mRtpSource.getSsrc() )
{ {
if ( ssrc != mRtpSource.getSsrc() ) if ( ssrc != mRtpSource.getSsrc() )
{ {
Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 ); return( -1 );
} }
} }
else if ( ssrc ) else if ( ssrc )
{ {
mRtpSource.setSsrc( ssrc ); mRtpSource.setSsrc( ssrc );
} }
if ( len > 1 ) if ( len > 1 )
{ {
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts ); //printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" ); //printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN); uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime ); mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
} }
break; break;
}
case RTCP_SDES :
{
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
while ( contentLen )
{
Debug( 5, "RTCP CL: %zd", contentLen );
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
{
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
for ( int i = 0; i < count; i++ )
{
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
Debug( 5, "RTCP Item length %d", item->len );
switch( item->type )
{
case RTCP_SDES_CNAME :
{
std::string cname( item->data, item->len );
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
break;
}
case RTCP_SDES_END :
case RTCP_SDES_NAME :
case RTCP_SDES_EMAIL :
case RTCP_SDES_PHONE :
case RTCP_SDES_LOC :
case RTCP_SDES_TOOL :
case RTCP_SDES_NOTE :
case RTCP_SDES_PRIV :
default :
{
Error( "Received unexpected SDES item type %d, ignoring", item->type );
return( -1 );
}
}
int paddedLen = 4+2+item->len+1; // Add null byte
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
Debug( 5, "RTCP PL:%d", paddedLen );
sdesPtr += paddedLen;
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
}
}
break;
}
case RTCP_BYE :
{
Debug( 5, "RTCP Got BYE" );
mStop = true;
break;
}
case RTCP_APP :
{
// Ignoring as per RFC 3550
Debug( 5, "Received RTCP_APP packet, ignoring.");
break;
}
case RTCP_RR :
{
Error( "Received RTCP_RR packet." );
return( -1 );
}
default :
{
// Ignore unknown packet types. Some cameras do this by design.
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
break;
}
} }
consumed = sizeof(uint32_t)*(len+1); case RTCP_SDES :
return( consumed ); {
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
while ( contentLen )
{
Debug( 5, "RTCP CL: %zd", contentLen );
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
{
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
for ( int i = 0; i < count; i++ )
{
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
Debug( 5, "RTCP Item length %d", item->len );
switch( item->type )
{
case RTCP_SDES_CNAME :
{
std::string cname( item->data, item->len );
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
break;
}
case RTCP_SDES_END :
case RTCP_SDES_NAME :
case RTCP_SDES_EMAIL :
case RTCP_SDES_PHONE :
case RTCP_SDES_LOC :
case RTCP_SDES_TOOL :
case RTCP_SDES_NOTE :
case RTCP_SDES_PRIV :
default :
{
Error( "Received unexpected SDES item type %d, ignoring", item->type );
return( -1 );
}
}
int paddedLen = 4+2+item->len+1; // Add null byte
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
Debug( 5, "RTCP PL:%d", paddedLen );
sdesPtr += paddedLen;
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
}
}
break;
}
case RTCP_BYE :
{
Debug( 5, "RTCP Got BYE" );
mStop = true;
break;
}
case RTCP_APP :
{
// Ignoring as per RFC 3550
Debug( 5, "Received RTCP_APP packet, ignoring.");
break;
}
case RTCP_RR :
{
Error( "Received RTCP_RR packet." );
return( -1 );
}
default :
{
// Ignore unknown packet types. Some cameras do this by design.
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
break;
}
}
consumed = sizeof(uint32_t)*(len+1);
return( consumed );
} }
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
{ {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.version = RTP_VERSION;
rtcpPacket->header.p = 0; rtcpPacket->header.p = 0;
rtcpPacket->header.pt = RTCP_RR; rtcpPacket->header.pt = RTCP_RR;
rtcpPacket->header.count = 1; rtcpPacket->header.count = 1;
rtcpPacket->header.lenN = htons(wordLen-1); rtcpPacket->header.lenN = htons(wordLen-1);
mRtpSource.updateRtcpStats(); mRtpSource.updateRtcpStats();
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets(); rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction(); rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq()); rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter()); rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
rtcpPacket->body.rr.rr[0].dlsrN = 0; rtcpPacket->body.rr.rr[0].dlsrN = 0;
return( wordLen*sizeof(uint32_t) ); return( wordLen*sizeof(uint32_t) );
} }
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
{ {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
const std::string &cname = mRtpSource.getCname(); const std::string &cname = mRtpSource.getCname();
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size(); int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.version = RTP_VERSION;
rtcpPacket->header.p = 0; rtcpPacket->header.p = 0;
rtcpPacket->header.pt = RTCP_SDES; rtcpPacket->header.pt = RTCP_SDES;
rtcpPacket->header.count = 1; rtcpPacket->header.count = 1;
rtcpPacket->header.lenN = htons(wordLen-1); rtcpPacket->header.lenN = htons(wordLen-1);
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1); rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME; rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
rtcpPacket->body.sdes.item[0].len = cname.size(); rtcpPacket->body.sdes.item[0].len = cname.size();
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
return( wordLen*sizeof(uint32_t) ); return( wordLen*sizeof(uint32_t) );
} }
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
{ {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.version = RTP_VERSION;
rtcpPacket->header.p = 0; rtcpPacket->header.p = 0;
rtcpPacket->header.pt = RTCP_BYE; rtcpPacket->header.pt = RTCP_BYE;
rtcpPacket->header.count = 1; rtcpPacket->header.count = 1;
rtcpPacket->header.lenN = htons(wordLen-1); rtcpPacket->header.lenN = htons(wordLen-1);
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
return( wordLen*sizeof(uint32_t) ); return( wordLen*sizeof(uint32_t) );
} }
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
{ {
unsigned char *bufferPtr = buffer; unsigned char *bufferPtr = buffer;
// u_int32 len; /* length of compound RTCP packet in words */ // u_int32 len; /* length of compound RTCP packet in words */
// rtcp_t *r; /* RTCP header */ // rtcp_t *r; /* RTCP header */
// rtcp_t *end; /* end of compound RTCP packet */ // rtcp_t *end; /* end of compound RTCP packet */
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) { // if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
// /* something wrong with packet format */ // /* something wrong with packet format */
// } // }
// end = (rtcp_t *)((u_int32 *)r + len); // end = (rtcp_t *)((u_int32 *)r + len);
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1); // do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
// while (r < end && r->common.version == 2); // while (r < end && r->common.version == 2);
// if (r != end) { // if (r != end) {
// /* something wrong with packet format */ // /* something wrong with packet format */
// } // }
while ( nBytes > 0 ) while ( nBytes > 0 )
{ {
int consumed = recvPacket( bufferPtr, nBytes ); int consumed = recvPacket( bufferPtr, nBytes );
if ( consumed <= 0 ) if ( consumed <= 0 )
break; break;
bufferPtr += consumed; bufferPtr += consumed;
nBytes -= consumed; nBytes -= consumed;
} }
return( nBytes ); return( nBytes );
} }
int RtpCtrlThread::run() int RtpCtrlThread::run()
{ {
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
SockAddrInet localAddr, remoteAddr; SockAddrInet localAddr, remoteAddr;
bool sendReports; bool sendReports;
UdpInetSocket rtpCtrlServer; UdpInetSocket rtpCtrlServer;
if ( mRtpSource.getLocalHost() != "" ) if ( mRtpSource.getLocalHost() != "" )
{
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
if ( !rtpCtrlServer.bind( localAddr ) )
Fatal( "Failed to bind RTCP server" );
sendReports = false;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
}
else
{
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
if ( !rtpCtrlServer.bind( localAddr ) )
Fatal( "Failed to bind RTCP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
if ( !rtpCtrlServer.connect( remoteAddr ) )
Fatal( "Failed to connect RTCP server" );
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
sendReports = true;
}
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
Select select( 10 );
select.addReader( &rtpCtrlServer );
unsigned char buffer[ZM_NETWORK_BUFSIZ];
time_t last_receive = time(NULL);
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
while ( !mStop && select.wait() >= 0 ) {
time_t now = time(NULL);
Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 )
{ {
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" ); if ( ! timeout ) {
if ( !rtpCtrlServer.bind( localAddr ) ) // With this code here, we will send an SDES and RR packet every 10 seconds
Fatal( "Failed to bind RTCP server" ); ssize_t nBytes;
sendReports = false; unsigned char *bufferPtr = buffer;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
Error( "Unable to send: %s", strerror( errno ) );
timeout = true;
continue;
} else {
//Error( "RTCP timed out" );
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
continue;
//break;
}
} else {
timeout = false;
last_receive = time(NULL);
} }
else for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
{ {
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" ); if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
if ( !rtpCtrlServer.bind( localAddr ) ) {
Fatal( "Failed to bind RTCP server" ); ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
if ( !rtpCtrlServer.connect( remoteAddr ) )
Fatal( "Failed to connect RTCP server" );
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
sendReports = true;
}
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets. if ( nBytes )
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
Select select( 10 );
select.addReader( &rtpCtrlServer );
unsigned char buffer[ZM_NETWORK_BUFSIZ];
time_t last_receive = time(NULL);
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
while ( !mStop && select.wait() >= 0 ) {
time_t now = time(NULL);
Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 )
{ {
if ( ! timeout ) { recvPackets( buffer, nBytes );
// With this code here, we will send an SDES and RR packet every 10 seconds
ssize_t nBytes;
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
Error( "Unable to send: %s", strerror( errno ) );
timeout = true;
continue;
} else {
//Error( "RTCP timed out" );
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
continue;
//break;
}
} else {
timeout = false;
last_receive = time(NULL);
}
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
{
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
{
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes ) if ( sendReports )
{ {
recvPackets( buffer, nBytes ); unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
if ( sendReports ) bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
{ Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
unsigned char *bufferPtr = buffer; if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); Error( "Unable to send: %s", strerror( errno ) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); }
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) } else {
Error( "Unable to send: %s", strerror( errno ) ); // Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); mStop = true;
} break;
} else {
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
mStop = true;
break;
}
}
else
{
Panic( "Barfed" );
}
} }
}
else
{
Panic( "Barfed" );
}
} }
rtpCtrlServer.close(); }
mRtspThread.stop(); rtpCtrlServer.close();
return( 0 ); mRtspThread.stop();
return( 0 );
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -25,7 +25,7 @@
#include "zm_thread.h" #include "zm_thread.h"
// Defined in ffmpeg rtp.h // Defined in ffmpeg rtp.h
//#define RTP_MAX_SDES 255 // maximum text length for SDES //#define RTP_MAX_SDES 255 // maximum text length for SDES
// Big-endian mask for version, padding bit and packet type pair // Big-endian mask for version, padding bit and packet type pair
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe) #define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
friend class RtspThread; friend class RtspThread;
private: private:
typedef enum typedef enum
{
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
RTCP_BYE = 203,
RTCP_APP = 204
} RtcpType;
typedef enum
{
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
RTCP_SDES_EMAIL = 3,
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8
} RtcpSdesType;
struct RtcpCommonHeader
{
uint8_t count:5; // varies by packet type
uint8_t p:1; // padding flag
uint8_t version:2; // protocol version
uint8_t pt; // RTCP packet type
uint16_t lenN; // pkt len in words, w/o this word, network order
};
// Reception report block
struct RtcpRr
{
uint32_t ssrcN; // data source being reported
int32_t lost:24; // cumul. no. pkts lost (signed!)
uint32_t fraction:8; // fraction lost since last SR/RR
uint32_t lastSeqN; // extended last seq. no. received, network order
uint32_t jitterN; // interarrival jitter, network order
uint32_t lsrN; // last SR packet from this source, network order
uint32_t dlsrN; // delay since last SR packet, network order
};
// SDES item
struct RtcpSdesItem
{
uint8_t type; // type of item (rtcp_sdes_type_t)
uint8_t len; // length of item (in octets)
char data[]; // text, not null-terminated
};
// RTCP packet
struct RtcpPacket
{
RtcpCommonHeader header; // common header
union
{ {
RTCP_SR = 200, // Sender Report (SR)
RTCP_RR = 201, struct Sr
RTCP_SDES = 202, {
RTCP_BYE = 203, uint32_t ssrcN; // sender generating this report, network order
RTCP_APP = 204 uint32_t ntpSecN; // NTP timestamp, network order
} RtcpType; uint32_t ntpFracN;
uint32_t rtpTsN; // RTP timestamp, network order
uint32_t pSentN; // packets sent, network order
uint32_t oSentN; // octets sent, network order
RtcpRr rr[]; // variable-length list
} sr;
typedef enum // Reception Report (RR)
{ struct Rr
RTCP_SDES_END = 0, {
RTCP_SDES_CNAME = 1, uint32_t ssrcN; // receiver generating this report
RTCP_SDES_NAME = 2, RtcpRr rr[]; // variable-length list
RTCP_SDES_EMAIL = 3, } rr;
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8
} RtcpSdesType;
struct RtcpCommonHeader // source description (SDES)
{ struct Sdes
uint8_t count:5; // varies by packet type {
uint8_t p:1; // padding flag uint32_t srcN; // first SSRC/CSRC
uint8_t version:2; // protocol version RtcpSdesItem item[]; // list of SDES items
uint8_t pt; // RTCP packet type } sdes;
uint16_t lenN; // pkt len in words, w/o this word, network order
};
// Reception report block // BYE
struct RtcpRr struct Bye
{ {
uint32_t ssrcN; // data source being reported uint32_t srcN[]; // list of sources
int32_t lost:24; // cumul. no. pkts lost (signed!) // can't express trailing text for reason (what does this mean? it's not even english!)
uint32_t fraction:8; // fraction lost since last SR/RR } bye;
uint32_t lastSeqN; // extended last seq. no. received, network order } body;
uint32_t jitterN; // interarrival jitter, network order };
uint32_t lsrN; // last SR packet from this source, network order
uint32_t dlsrN; // delay since last SR packet, network order
};
// SDES item
struct RtcpSdesItem
{
uint8_t type; // type of item (rtcp_sdes_type_t)
uint8_t len; // length of item (in octets)
char data[]; // text, not null-terminated
};
// RTCP packet
struct RtcpPacket
{
RtcpCommonHeader header; // common header
union
{
// Sender Report (SR)
struct Sr
{
uint32_t ssrcN; // sender generating this report, network order
uint32_t ntpSecN; // NTP timestamp, network order
uint32_t ntpFracN;
uint32_t rtpTsN; // RTP timestamp, network order
uint32_t pSentN; // packets sent, network order
uint32_t oSentN; // octets sent, network order
RtcpRr rr[]; // variable-length list
} sr;
// Reception Report (RR)
struct Rr
{
uint32_t ssrcN; // receiver generating this report
RtcpRr rr[]; // variable-length list
} rr;
// source description (SDES)
struct Sdes
{
uint32_t srcN; // first SSRC/CSRC
RtcpSdesItem item[]; // list of SDES items
} sdes;
// BYE
struct Bye
{
uint32_t srcN[]; // list of sources
// can't express trailing text for reason (what does this mean? it's not even english!)
} bye;
} body;
};
private: private:
RtspThread &mRtspThread; RtspThread &mRtspThread;
RtpSource &mRtpSource; RtpSource &mRtpSource;
int mPort; int mPort;
bool mStop; bool mStop;
private: private:
int recvPacket( const unsigned char *packet, ssize_t packetLen ); int recvPacket( const unsigned char *packet, ssize_t packetLen );
int generateRr( const unsigned char *packet, ssize_t packetLen ); int generateRr( const unsigned char *packet, ssize_t packetLen );
int generateSdes( const unsigned char *packet, ssize_t packetLen ); int generateSdes( const unsigned char *packet, ssize_t packetLen );
int generateBye( const unsigned char *packet, ssize_t packetLen ); int generateBye( const unsigned char *packet, ssize_t packetLen );
int recvPackets( unsigned char *buffer, ssize_t nBytes ); int recvPackets( unsigned char *buffer, ssize_t nBytes );
int run(); int run();
public: public:
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
void stop() void stop()
{ {
mStop = true; mStop = true;
} }
}; };
#endif // ZM_RTP_CTRL_H #endif // ZM_RTP_CTRL_H

View File

@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen ) bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
{ {
const RtpDataHeader *rtpHeader; const RtpDataHeader *rtpHeader;
rtpHeader = (RtpDataHeader *)packet; rtpHeader = (RtpDataHeader *)packet;
//printf( "D: " ); //printf( "D: " );
//for ( int i = 0; i < 32; i++ ) //for ( int i = 0; i < 32; i++ )
//printf( "%02x ", (unsigned char)packet[i] ); //printf( "%02x ", (unsigned char)packet[i] );
//printf( "\n" ); //printf( "\n" );
Debug( 5, "Ver: %d", rtpHeader->version ); Debug( 5, "Ver: %d", rtpHeader->version );
Debug( 5, "P: %d", rtpHeader->p ); Debug( 5, "P: %d", rtpHeader->p );
Debug( 5, "Pt: %d", rtpHeader->pt ); Debug( 5, "Pt: %d", rtpHeader->pt );
Debug( 5, "Mk: %d", rtpHeader->m ); Debug( 5, "Mk: %d", rtpHeader->m );
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) ); Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) ); Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) ); Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
//unsigned short seq = ntohs(rtpHeader->seqN); //unsigned short seq = ntohs(rtpHeader->seqN);
unsigned long ssrc = ntohl(rtpHeader->ssrcN); unsigned long ssrc = ntohl(rtpHeader->ssrcN);
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
{ {
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc ); Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
return( false ); return( false );
} }
return( mRtpSource.handlePacket( packet, packetLen ) ); return( mRtpSource.handlePacket( packet, packetLen ) );
} }
int RtpDataThread::run() int RtpDataThread::run()
{ {
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() ); Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
SockAddrInet localAddr; SockAddrInet localAddr;
UdpInetServer rtpDataSocket; UdpInetServer rtpDataSocket;
if ( mRtpSource.getLocalHost() != "" ) if ( mRtpSource.getLocalHost() != "" )
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" ); localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
else else
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" ); localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
if ( !rtpDataSocket.bind( localAddr ) ) if ( !rtpDataSocket.bind( localAddr ) )
Fatal( "Failed to bind RTP server" ); Fatal( "Failed to bind RTP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
Select select( 3 ); Select select( 3 );
select.addReader( &rtpDataSocket ); select.addReader( &rtpDataSocket );
unsigned char buffer[ZM_NETWORK_BUFSIZ]; unsigned char buffer[ZM_NETWORK_BUFSIZ];
while ( !mStop && select.wait() >= 0 ) while ( !mStop && select.wait() >= 0 )
{ {
if ( mStop ) if ( mStop )
break; break;
Select::CommsList readable = select.getReadable(); Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 ) if ( readable.size() == 0 )
{
Error( "RTP timed out" );
mStop = true;
break;
}
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
{
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
{
int nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes )
{ {
Error( "RTP timed out" ); recvPacket( buffer, nBytes );
mStop = true;
break;
} }
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) else
{ {
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) ) mStop = true;
{ break;
int nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes )
{
recvPacket( buffer, nBytes );
}
else
{
mStop = true;
break;
}
}
else
{
Panic( "Barfed" );
}
} }
} }
rtpDataSocket.close(); else
mRtspThread.stop(); {
return( 0 ); Panic( "Barfed" );
}
}
}
rtpDataSocket.close();
mRtspThread.stop();
return( 0 );
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -30,16 +30,16 @@ class RtpSource;
struct RtpDataHeader struct RtpDataHeader
{ {
uint8_t cc:4; // CSRC count uint8_t cc:4; // CSRC count
uint8_t x:1; // header extension flag uint8_t x:1; // header extension flag
uint8_t p:1; // padding flag uint8_t p:1; // padding flag
uint8_t version:2; // protocol version uint8_t version:2; // protocol version
uint8_t pt:7; // payload type uint8_t pt:7; // payload type
uint8_t m:1; // marker bit uint8_t m:1; // marker bit
uint16_t seqN; // sequence number, network order uint16_t seqN; // sequence number, network order
uint32_t timestampN; // timestamp, network order uint32_t timestampN; // timestamp, network order
uint32_t ssrcN; // synchronization source, network order uint32_t ssrcN; // synchronization source, network order
uint32_t csrc[]; // optional CSRC list uint32_t csrc[]; // optional CSRC list
}; };
class RtpDataThread : public Thread class RtpDataThread : public Thread
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
friend class RtspThread; friend class RtspThread;
private: private:
RtspThread &mRtspThread; RtspThread &mRtspThread;
RtpSource &mRtpSource; RtpSource &mRtpSource;
bool mStop; bool mStop;
private: private:
bool recvPacket( const unsigned char *packet, size_t packetLen ); bool recvPacket( const unsigned char *packet, size_t packetLen );
int run(); int run();
public: public:
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ); RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
void stop() void stop()
{ {
mStop = true; mStop = true;
} }
}; };
#endif // ZM_RTP_DATA_H #endif // ZM_RTP_DATA_H

View File

@ -27,358 +27,358 @@
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
mId( id ), mId( id ),
mSsrc( ssrc ), mSsrc( ssrc ),
mLocalHost( localHost ), mLocalHost( localHost ),
mRemoteHost( remoteHost ), mRemoteHost( remoteHost ),
mRtpClock( rtpClock ), mRtpClock( rtpClock ),
mCodecId( codecId ), mCodecId( codecId ),
mFrame( 65536 ), mFrame( 65536 ),
mFrameCount( 0 ), mFrameCount( 0 ),
mFrameGood( true ), mFrameGood( true ),
mFrameReady( false ), mFrameReady( false ),
mFrameProcessed( false ) mFrameProcessed( false )
{ {
char hostname[256] = ""; char hostname[256] = "";
gethostname( hostname, sizeof(hostname) ); gethostname( hostname, sizeof(hostname) );
mCname = stringtf( "zm-%d@%s", mId, hostname ); mCname = stringtf( "zm-%d@%s", mId, hostname );
Debug( 3, "RTP CName = %s", mCname.c_str() ); Debug( 3, "RTP CName = %s", mCname.c_str() );
init( seq ); init( seq );
mMaxSeq = seq - 1; mMaxSeq = seq - 1;
mProbation = MIN_SEQUENTIAL; mProbation = MIN_SEQUENTIAL;
mLocalPortChans[0] = localPortBase; mLocalPortChans[0] = localPortBase;
mLocalPortChans[1] = localPortBase+1; mLocalPortChans[1] = localPortBase+1;
mRemotePortChans[0] = remotePortBase; mRemotePortChans[0] = remotePortBase;
mRemotePortChans[1] = remotePortBase+1; mRemotePortChans[1] = remotePortBase+1;
mRtpFactor = mRtpClock; mRtpFactor = mRtpClock;
mBaseTimeReal = tvNow(); mBaseTimeReal = tvNow();
mBaseTimeNtp = tvZero(); mBaseTimeNtp = tvZero();
mBaseTimeRtp = rtpTime; mBaseTimeRtp = rtpTime;
mLastSrTimeReal = tvZero(); mLastSrTimeReal = tvZero();
mLastSrTimeNtp = tvZero(); mLastSrTimeNtp = tvZero();
mLastSrTimeRtp = 0; mLastSrTimeRtp = 0;
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
} }
void RtpSource::init( uint16_t seq ) void RtpSource::init( uint16_t seq )
{ {
Debug( 3, "Initialising sequence" ); Debug( 3, "Initialising sequence" );
mBaseSeq = seq; mBaseSeq = seq;
mMaxSeq = seq; mMaxSeq = seq;
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
mCycles = 0; mCycles = 0;
mReceivedPackets = 0; mReceivedPackets = 0;
mReceivedPrior = 0; mReceivedPrior = 0;
mExpectedPrior = 0; mExpectedPrior = 0;
// other initialization // other initialization
mJitter = 0; mJitter = 0;
mTransit = 0; mTransit = 0;
} }
bool RtpSource::updateSeq( uint16_t seq ) bool RtpSource::updateSeq( uint16_t seq )
{ {
uint16_t uDelta = seq - mMaxSeq; uint16_t uDelta = seq - mMaxSeq;
// Source is not valid until MIN_SEQUENTIAL packets with // Source is not valid until MIN_SEQUENTIAL packets with
// sequential sequence numbers have been received. // sequential sequence numbers have been received.
Debug( 5, "Seq: %d", seq ); Debug( 5, "Seq: %d", seq );
if ( mProbation) if ( mProbation)
{
// packet is in sequence
if ( seq == mMaxSeq + 1)
{ {
// packet is in sequence Debug( 3, "Sequence in probation %d, in sequence", mProbation );
if ( seq == mMaxSeq + 1) mProbation--;
{ mMaxSeq = seq;
Debug( 3, "Sequence in probation %d, in sequence", mProbation ); if ( mProbation == 0 )
mProbation--; {
mMaxSeq = seq; init( seq );
if ( mProbation == 0 ) mReceivedPackets++;
{
init( seq );
mReceivedPackets++;
return( true );
}
}
else
{
Warning( "Sequence in probation %d, out of sequence", mProbation );
mProbation = MIN_SEQUENTIAL - 1;
mMaxSeq = seq;
return( false );
}
return( true ); return( true );
} }
else if ( uDelta < MAX_DROPOUT )
{
if ( uDelta == 1 )
{
Debug( 3, "Packet in sequence, gap %d", uDelta );
}
else
{
Warning( "Packet in sequence, gap %d", uDelta );
}
// in order, with permissible gap
if ( seq < mMaxSeq )
{
// Sequence number wrapped - count another 64K cycle.
mCycles += RTP_SEQ_MOD;
}
mMaxSeq = seq;
}
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
{
Warning( "Packet out of sequence, gap %d", uDelta );
// the sequence number made a very large jump
if ( seq == mBadSeq )
{
Debug( 3, "Restarting sequence" );
// Two sequential packets -- assume that the other side
// restarted without telling us so just re-sync
// (i.e., pretend this was the first packet).
init( seq );
}
else
{
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
return( false );
}
} }
else else
{ {
Warning( "Packet duplicate or reordered, gap %d", uDelta ); Warning( "Sequence in probation %d, out of sequence", mProbation );
// duplicate or reordered packet mProbation = MIN_SEQUENTIAL - 1;
return( false ); mMaxSeq = seq;
return( false );
} }
mReceivedPackets++; return( true );
return( uDelta==1?true:false ); }
else if ( uDelta < MAX_DROPOUT )
{
if ( uDelta == 1 )
{
Debug( 3, "Packet in sequence, gap %d", uDelta );
}
else
{
Warning( "Packet in sequence, gap %d", uDelta );
}
// in order, with permissible gap
if ( seq < mMaxSeq )
{
// Sequence number wrapped - count another 64K cycle.
mCycles += RTP_SEQ_MOD;
}
mMaxSeq = seq;
}
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
{
Warning( "Packet out of sequence, gap %d", uDelta );
// the sequence number made a very large jump
if ( seq == mBadSeq )
{
Debug( 3, "Restarting sequence" );
// Two sequential packets -- assume that the other side
// restarted without telling us so just re-sync
// (i.e., pretend this was the first packet).
init( seq );
}
else
{
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
return( false );
}
}
else
{
Warning( "Packet duplicate or reordered, gap %d", uDelta );
// duplicate or reordered packet
return( false );
}
mReceivedPackets++;
return( uDelta==1?true:false );
} }
void RtpSource::updateJitter( const RtpDataHeader *header ) void RtpSource::updateJitter( const RtpDataHeader *header )
{ {
if ( mRtpFactor > 0 ) if ( mRtpFactor > 0 )
{ {
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
Debug( 5, "Local RTP time = %x", localTimeRtp ); Debug( 5, "Local RTP time = %x", localTimeRtp );
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
Debug( 5, "Packet transit RTP time = %x", packetTransit ); Debug( 5, "Packet transit RTP time = %x", packetTransit );
if ( mTransit > 0 ) if ( mTransit > 0 )
{
// Jitter
int d = packetTransit - mTransit;
Debug( 5, "Jitter D = %d", d );
if ( d < 0 )
d = -d;
//mJitter += (1./16.) * ((double)d - mJitter);
mJitter += d - ((mJitter + 8) >> 4);
}
mTransit = packetTransit;
}
else
{ {
mJitter = 0; // Jitter
int d = packetTransit - mTransit;
Debug( 5, "Jitter D = %d", d );
if ( d < 0 )
d = -d;
//mJitter += (1./16.) * ((double)d - mJitter);
mJitter += d - ((mJitter + 8) >> 4);
} }
Debug( 5, "RTP Jitter: %d", mJitter ); mTransit = packetTransit;
}
else
{
mJitter = 0;
}
Debug( 5, "RTP Jitter: %d", mJitter );
} }
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
{ {
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
if ( mBaseTimeNtp.tv_sec == 0 )
{
mBaseTimeReal = tvNow();
mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime;
}
else if ( !mRtpClock )
{
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
if ( mBaseTimeNtp.tv_sec == 0 )
{
mBaseTimeReal = tvNow();
mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime;
}
else if ( !mRtpClock )
{
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
//Debug( 5, "Real-diff: %.6f", diffRealTime ); //Debug( 5, "Real-diff: %.6f", diffRealTime );
Debug( 5, "NTP-diff: %.6f", diffNtpTime ); Debug( 5, "NTP-diff: %.6f", diffNtpTime );
Debug( 5, "RTP-diff: %d", diffRtpTime ); Debug( 5, "RTP-diff: %d", diffRtpTime );
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
Debug( 5, "RTPfactor: %d", mRtpFactor ); Debug( 5, "RTPfactor: %d", mRtpFactor );
} }
mLastSrTimeNtpSecs = ntpTimeSecs; mLastSrTimeNtpSecs = ntpTimeSecs;
mLastSrTimeNtpFrac = ntpTimeFrac; mLastSrTimeNtpFrac = ntpTimeFrac;
mLastSrTimeNtp = ntpTime; mLastSrTimeNtp = ntpTime;
mLastSrTimeRtp = rtpTime; mLastSrTimeRtp = rtpTime;
} }
void RtpSource::updateRtcpStats() void RtpSource::updateRtcpStats()
{ {
uint32_t extendedMax = mCycles + mMaxSeq; uint32_t extendedMax = mCycles + mMaxSeq;
mExpectedPackets = extendedMax - mBaseSeq + 1; mExpectedPackets = extendedMax - mBaseSeq + 1;
Debug( 5, "Expected packets = %d", mExpectedPackets ); Debug( 5, "Expected packets = %d", mExpectedPackets );
// The number of packets lost is defined to be the number of packets // The number of packets lost is defined to be the number of packets
// expected less the number of packets actually received: // expected less the number of packets actually received:
mLostPackets = mExpectedPackets - mReceivedPackets; mLostPackets = mExpectedPackets - mReceivedPackets;
Debug( 5, "Lost packets = %d", mLostPackets ); Debug( 5, "Lost packets = %d", mLostPackets );
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
Debug( 5, "Expected interval = %d", expectedInterval ); Debug( 5, "Expected interval = %d", expectedInterval );
mExpectedPrior = mExpectedPackets; mExpectedPrior = mExpectedPackets;
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
Debug( 5, "Received interval = %d", receivedInterval ); Debug( 5, "Received interval = %d", receivedInterval );
mReceivedPrior = mReceivedPackets; mReceivedPrior = mReceivedPackets;
uint32_t lostInterval = expectedInterval - receivedInterval; uint32_t lostInterval = expectedInterval - receivedInterval;
Debug( 5, "Lost interval = %d", lostInterval ); Debug( 5, "Lost interval = %d", lostInterval );
if ( expectedInterval == 0 || lostInterval <= 0 ) if ( expectedInterval == 0 || lostInterval <= 0 )
mLostFraction = 0; mLostFraction = 0;
else else
mLostFraction = (lostInterval << 8) / expectedInterval; mLostFraction = (lostInterval << 8) / expectedInterval;
Debug( 5, "Lost fraction = %d", mLostFraction ); Debug( 5, "Lost fraction = %d", mLostFraction );
} }
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
{ {
const RtpDataHeader *rtpHeader; const RtpDataHeader *rtpHeader;
rtpHeader = (RtpDataHeader *)packet; rtpHeader = (RtpDataHeader *)packet;
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);
bool thisM = rtpHeader->m || h264FragmentEnd; bool thisM = rtpHeader->m || h264FragmentEnd;
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) if ( updateSeq( ntohs(rtpHeader->seqN) ) )
{
Hexdump( 4, packet+rtpHeaderSize, 16 );
if ( mFrameGood )
{ {
Hexdump( 4, packet+rtpHeaderSize, 16 ); int extraHeader = 0;
if ( mFrameGood ) if( mCodecId == AV_CODEC_ID_H264 )
{
int nalType = (packet[rtpHeaderSize] & 0x1f);
switch (nalType)
{ {
int extraHeader = 0; case 24:
{
if( mCodecId == AV_CODEC_ID_H264 ) extraHeader = 2;
break;
}
case 25: case 26: case 27:
{
extraHeader = 3;
break;
}
// FU-A and FU-B
case 28: case 29:
{
// Is this NAL the first NAL in fragmentation sequence
if ( packet[rtpHeaderSize+1] & 0x80 )
{ {
int nalType = (packet[rtpHeaderSize] & 0x1f); // Now we will form new header of frame
mFrame.append( "\x0\x0\x1\x0", 4 );
switch (nalType) // Reconstruct NAL header from FU headers
{ *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
case 24: (packet[rtpHeaderSize] & 0xe0);
{
extraHeader = 2;
break;
}
case 25: case 26: case 27:
{
extraHeader = 3;
break;
}
// FU-A and FU-B
case 28: case 29:
{
// Is this NAL the first NAL in fragmentation sequence
if ( packet[rtpHeaderSize+1] & 0x80 )
{
// Now we will form new header of frame
mFrame.append( "\x0\x0\x1\x0", 4 );
// Reconstruct NAL header from FU headers
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
(packet[rtpHeaderSize] & 0xe0);
}
extraHeader = 2;
break;
}
}
// Append NAL frame start code
if ( !mFrame.size() )
mFrame.append( "\x0\x0\x1", 3 );
} }
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
} extraHeader = 2;
break;
Hexdump( 4, mFrame.head(), 16 ); }
if ( thisM )
{
if ( mFrameGood )
{
Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
mFrameProcessed.setValueImmediate( false );
mFrameReady.updateValueSignal( true );
if ( !mFrameProcessed.getValueImmediate() )
{
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
if( count > 1 )
return( false );
}
mFrameCount++;
}
else
{
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
}
mFrame.clear();
} }
// Append NAL frame start code
if ( !mFrame.size() )
mFrame.append( "\x0\x0\x1", 3 );
}
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
} }
else
{ Hexdump( 4, mFrame.head(), 16 );
if ( mFrame.size() )
{
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
}
else
{
Warning( "Discarding frame %d", mFrameCount );
}
mFrameGood = false;
mFrame.clear();
}
if ( thisM ) if ( thisM )
{ {
mFrameGood = true; if ( mFrameGood )
prevM = true; {
Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
mFrameProcessed.setValueImmediate( false );
mFrameReady.updateValueSignal( true );
if ( !mFrameProcessed.getValueImmediate() )
{
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
if( count > 1 )
return( false );
}
mFrameCount++;
}
else
{
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
}
mFrame.clear();
}
}
else
{
if ( mFrame.size() )
{
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
} }
else else
prevM = false; {
Warning( "Discarding frame %d", mFrameCount );
}
mFrameGood = false;
mFrame.clear();
}
if ( thisM )
{
mFrameGood = true;
prevM = true;
}
else
prevM = false;
updateJitter( rtpHeader ); updateJitter( rtpHeader );
return( true ); return( true );
} }
bool RtpSource::getFrame( Buffer &buffer ) bool RtpSource::getFrame( Buffer &buffer )
{ {
Debug( 3, "Getting frame" ); Debug( 3, "Getting frame" );
if ( !mFrameReady.getValueImmediate() ) if ( !mFrameReady.getValueImmediate() )
{ {
// Allow for a couple of spurious returns // Allow for a couple of spurious returns
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
if ( count > 1 ) if ( count > 1 )
return( false ); return( false );
} }
buffer = mFrame; buffer = mFrame;
mFrameReady.setValueImmediate( false ); mFrameReady.setValueImmediate( false );
mFrameProcessed.updateValueSignal( true ); mFrameProcessed.updateValueSignal( true );
Debug( 3, "Copied %d bytes", buffer.size() ); Debug( 3, "Copied %d bytes", buffer.size() );
return( true ); return( true );
} }
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC

View File

@ -35,152 +35,152 @@ struct RtpDataHeader;
class RtpSource class RtpSource
{ {
public: public:
typedef enum { EMPTY, FILLING, READY } FrameState; typedef enum { EMPTY, FILLING, READY } FrameState;
private: private:
static const int RTP_SEQ_MOD = 1<<16; static const int RTP_SEQ_MOD = 1<<16;
static const int MAX_DROPOUT = 3000; static const int MAX_DROPOUT = 3000;
static const int MAX_MISORDER = 100; static const int MAX_MISORDER = 100;
static const int MIN_SEQUENTIAL = 2; static const int MIN_SEQUENTIAL = 2;
private: private:
// Identity // Identity
int mId; // General id (usually monitor id) int mId; // General id (usually monitor id)
std::string mCname; // Canonical name, for SDES std::string mCname; // Canonical name, for SDES
// RTP/RTCP fields // RTP/RTCP fields
uint32_t mSsrc; uint32_t mSsrc;
uint16_t mMaxSeq; // highest seq. number seen uint16_t mMaxSeq; // highest seq. number seen
uint32_t mCycles; // shifted count of seq. number cycles uint32_t mCycles; // shifted count of seq. number cycles
uint32_t mBaseSeq; // base seq number uint32_t mBaseSeq; // base seq number
uint32_t mBadSeq; // last 'bad' seq number + 1 uint32_t mBadSeq; // last 'bad' seq number + 1
uint32_t mProbation; // sequ. packets till source is valid uint32_t mProbation; // sequ. packets till source is valid
uint32_t mReceivedPackets; // packets received uint32_t mReceivedPackets; // packets received
uint32_t mExpectedPrior; // packet expected at last interval uint32_t mExpectedPrior; // packet expected at last interval
uint32_t mReceivedPrior; // packet received at last interval uint32_t mReceivedPrior; // packet received at last interval
uint32_t mTransit; // relative trans time for prev pkt uint32_t mTransit; // relative trans time for prev pkt
uint32_t mJitter; // estimated jitter uint32_t mJitter; // estimated jitter
// Ports/Channels // Ports/Channels
std::string mLocalHost; std::string mLocalHost;
int mLocalPortChans[2]; int mLocalPortChans[2];
std::string mRemoteHost; std::string mRemoteHost;
int mRemotePortChans[2]; int mRemotePortChans[2];
// Time keys // Time keys
uint32_t mRtpClock; uint32_t mRtpClock;
uint32_t mRtpFactor; uint32_t mRtpFactor;
struct timeval mBaseTimeReal; struct timeval mBaseTimeReal;
struct timeval mBaseTimeNtp; struct timeval mBaseTimeNtp;
uint32_t mBaseTimeRtp; uint32_t mBaseTimeRtp;
struct timeval mLastSrTimeReal; struct timeval mLastSrTimeReal;
uint32_t mLastSrTimeNtpSecs; uint32_t mLastSrTimeNtpSecs;
uint32_t mLastSrTimeNtpFrac; uint32_t mLastSrTimeNtpFrac;
struct timeval mLastSrTimeNtp; struct timeval mLastSrTimeNtp;
uint32_t mLastSrTimeRtp; uint32_t mLastSrTimeRtp;
// Stats, intermittently updated // Stats, intermittently updated
uint32_t mExpectedPackets; uint32_t mExpectedPackets;
uint32_t mLostPackets; uint32_t mLostPackets;
uint8_t mLostFraction; uint8_t mLostFraction;
_AVCODECID mCodecId; _AVCODECID mCodecId;
Buffer mFrame; Buffer mFrame;
int mFrameCount; int mFrameCount;
bool mFrameGood; bool mFrameGood;
bool prevM; bool prevM;
ThreadData<bool> mFrameReady; ThreadData<bool> mFrameReady;
ThreadData<bool> mFrameProcessed; ThreadData<bool> mFrameProcessed;
private: private:
void init( uint16_t seq ); void init( uint16_t seq );
public: public:
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ); RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
bool updateSeq( uint16_t seq ); bool updateSeq( uint16_t seq );
void updateJitter( const RtpDataHeader *header ); void updateJitter( const RtpDataHeader *header );
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ); void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
void updateRtcpStats(); void updateRtcpStats();
bool handlePacket( const unsigned char *packet, size_t packetLen ); bool handlePacket( const unsigned char *packet, size_t packetLen );
uint32_t getSsrc() const uint32_t getSsrc() const
{ {
return( mSsrc ); return( mSsrc );
} }
void setSsrc( uint32_t ssrc ) void setSsrc( uint32_t ssrc )
{ {
mSsrc = ssrc; mSsrc = ssrc;
} }
bool getFrame( Buffer &buffer ); bool getFrame( Buffer &buffer );
const std::string &getCname() const const std::string &getCname() const
{ {
return( mCname ); return( mCname );
} }
const std::string &getLocalHost() const const std::string &getLocalHost() const
{ {
return( mLocalHost ); return( mLocalHost );
} }
int getLocalDataPort() const int getLocalDataPort() const
{ {
return( mLocalPortChans[0] ); return( mLocalPortChans[0] );
} }
int getLocalCtrlPort() const int getLocalCtrlPort() const
{ {
return( mLocalPortChans[1] ); return( mLocalPortChans[1] );
} }
const std::string &getRemoteHost() const const std::string &getRemoteHost() const
{ {
return( mRemoteHost ); return( mRemoteHost );
} }
int getRemoteDataPort() const int getRemoteDataPort() const
{ {
return( mRemotePortChans[0] ); return( mRemotePortChans[0] );
} }
int getRemoteCtrlPort() const int getRemoteCtrlPort() const
{ {
return( mRemotePortChans[1] ); return( mRemotePortChans[1] );
} }
uint32_t getMaxSeq() const uint32_t getMaxSeq() const
{ {
return( mCycles + mMaxSeq ); return( mCycles + mMaxSeq );
} }
uint32_t getExpectedPackets() const uint32_t getExpectedPackets() const
{ {
return( mExpectedPackets ); return( mExpectedPackets );
} }
uint32_t getLostPackets() const uint32_t getLostPackets() const
{ {
return( mLostPackets ); return( mLostPackets );
} }
uint8_t getLostFraction() const uint8_t getLostFraction() const
{ {
return( mLostFraction ); return( mLostFraction );
} }
uint32_t getJitter() const uint32_t getJitter() const
{ {
return( mJitter >> 4 ); return( mJitter >> 4 );
} }
uint32_t getLastSrTimestamp() const uint32_t getLastSrTimestamp() const
{ {
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) ); return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
} }
}; };
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC

File diff suppressed because it is too large Load Diff

View File

@ -34,110 +34,110 @@
class RtspThread : public Thread class RtspThread : public Thread
{ {
public: public:
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod; typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist; typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
private: private:
typedef std::set<int> PortSet; typedef std::set<int> PortSet;
typedef std::set<uint32_t> SsrcSet; typedef std::set<uint32_t> SsrcSet;
typedef std::map<uint32_t,RtpSource *> SourceMap; typedef std::map<uint32_t,RtpSource *> SourceMap;
private: private:
static int smMinDataPort; static int smMinDataPort;
static int smMaxDataPort; static int smMaxDataPort;
static PortSet smLocalSsrcs; static PortSet smLocalSsrcs;
static PortSet smAssignedPorts; static PortSet smAssignedPorts;
private: private:
int mId; int mId;
RtspMethod mMethod; RtspMethod mMethod;
std::string mProtocol; std::string mProtocol;
std::string mHost; std::string mHost;
std::string mPort; std::string mPort;
std::string mPath; std::string mPath;
bool mRtspDescribe; bool mRtspDescribe;
std::string mUrl; std::string mUrl;
// Reworked authentication system // Reworked authentication system
// First try without authentication, even if we have a username and password // First try without authentication, even if we have a username and password
// on receiving a 401 response, select authentication method (basic or digest) // on receiving a 401 response, select authentication method (basic or digest)
// fill required fields and set needAuth // fill required fields and set needAuth
// subsequent requests can set the required authentication header. // subsequent requests can set the required authentication header.
bool mNeedAuth; bool mNeedAuth;
int respCode; int respCode;
zm::Authenticator* mAuthenticator; zm::Authenticator* mAuthenticator;
std::string mHttpSession; ///< Only for RTSP over HTTP sessions std::string mHttpSession; ///< Only for RTSP over HTTP sessions
TcpInetClient mRtspSocket; TcpInetClient mRtspSocket;
TcpInetClient mRtspSocket2; TcpInetClient mRtspSocket2;
SourceMap mSources; SourceMap mSources;
SessionDescriptor *mSessDesc; SessionDescriptor *mSessDesc;
AVFormatContext *mFormatContext; AVFormatContext *mFormatContext;
uint16_t mSeq; uint16_t mSeq;
uint32_t mSession; uint32_t mSession;
uint32_t mSsrc; uint32_t mSsrc;
int mRemotePorts[2]; int mRemotePorts[2];
int mRemoteChannels[2]; int mRemoteChannels[2];
RtspDist mDist; RtspDist mDist;
unsigned long mRtpTime; unsigned long mRtpTime;
bool mStop; bool mStop;
private: private:
bool sendCommand( std::string message ); bool sendCommand( std::string message );
bool recvResponse( std::string &response ); bool recvResponse( std::string &response );
void checkAuthResponse(std::string &response); void checkAuthResponse(std::string &response);
public: public:
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe ); RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
~RtspThread(); ~RtspThread();
public: public:
int requestPorts(); int requestPorts();
void releasePorts( int port ); void releasePorts( int port );
bool isValidSsrc( uint32_t ssrc ); bool isValidSsrc( uint32_t ssrc );
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header ); bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
uint32_t getSsrc() const uint32_t getSsrc() const
{ {
return( mSsrc ); return( mSsrc );
} }
bool hasSources() const bool hasSources() const
{ {
return( !mSources.empty() ); return( !mSources.empty() );
} }
AVFormatContext *getFormatContext() AVFormatContext *getFormatContext()
{ {
return( mFormatContext ); return( mFormatContext );
} }
bool getFrame( Buffer &frame ) bool getFrame( Buffer &frame )
{ {
SourceMap::iterator iter = mSources.begin(); SourceMap::iterator iter = mSources.begin();
if ( iter == mSources.end() ) if ( iter == mSources.end() )
return( false ); return( false );
return( iter->second->getFrame( frame ) ); return( iter->second->getFrame( frame ) );
} }
int run(); int run();
void stop() void stop()
{ {
mStop = true; mStop = true;
} }
bool stopped() const bool stopped() const
{ {
return( mStop ); return( mStop );
} }
}; };
#endif // ZM_RTSP_H #endif // ZM_RTSP_H

View File

@ -28,206 +28,206 @@ namespace zm {
Authenticator::Authenticator(std::string &username, std::string password) { Authenticator::Authenticator(std::string &username, std::string password) {
#ifdef HAVE_GCRYPT_H #ifdef HAVE_GCRYPT_H
// Special initialisation for libgcrypt // Special initialisation for libgcrypt
if ( !gcry_check_version( GCRYPT_VERSION ) ) if ( !gcry_check_version( GCRYPT_VERSION ) )
{ {
Fatal( "Unable to initialise libgcrypt" ); Fatal( "Unable to initialise libgcrypt" );
} }
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
#endif // HAVE_GCRYPT_H #endif // HAVE_GCRYPT_H
fAuthMethod = AUTH_UNDEFINED; fAuthMethod = AUTH_UNDEFINED;
fUsername = username; fUsername = username;
fPassword = password; fPassword = password;
nc = 1; nc = 1;
fCnonce = "0a4f113b"; fCnonce = "0a4f113b";
} }
Authenticator::~Authenticator() { Authenticator::~Authenticator() {
reset(); reset();
} }
void Authenticator::reset() { void Authenticator::reset() {
fRealm.clear(); fRealm.clear();
fNonce.clear(); fNonce.clear();
fUsername.clear(); fUsername.clear();
fPassword.clear(); fPassword.clear();
fAuthMethod = AUTH_UNDEFINED; fAuthMethod = AUTH_UNDEFINED;
} }
void Authenticator::authHandleHeader(std::string headerData) void Authenticator::authHandleHeader(std::string headerData)
{ {
const char* basic_match = "Basic "; const char* basic_match = "Basic ";
const char* digest_match = "Digest "; const char* digest_match = "Digest ";
size_t digest_match_len = strlen(digest_match); size_t digest_match_len = strlen(digest_match);
// Check if basic auth // Check if basic auth
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0) if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
{
fAuthMethod = AUTH_BASIC;
Debug( 2, "Set authMethod to Basic");
}
// Check if digest auth
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
{
fAuthMethod = AUTH_DIGEST;
Debug( 2, "Set authMethod to Digest");
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
// subparts are key="value"
for ( size_t i = 0; i < subparts.size(); i++ )
{ {
fAuthMethod = AUTH_BASIC; StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
Debug( 2, "Set authMethod to Basic"); std::string key = trimSpaces( kvPair[0] );
} if (key == "realm") {
// Check if digest auth fRealm = trimSet( kvPair[1], "\"");
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0) continue;
{ }
fAuthMethod = AUTH_DIGEST; if (key == "nonce") {
Debug( 2, "Set authMethod to Digest"); fNonce = trimSet( kvPair[1], "\"");
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ","); continue;
// subparts are key="value" }
for ( size_t i = 0; i < subparts.size(); i++ ) if (key == "qop") {
{ fQop = trimSet( kvPair[1], "\"");
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" ); continue;
std::string key = trimSpaces( kvPair[0] ); }
if (key == "realm") {
fRealm = trimSet( kvPair[1], "\"");
continue;
}
if (key == "nonce") {
fNonce = trimSet( kvPair[1], "\"");
continue;
}
if (key == "qop") {
fQop = trimSet( kvPair[1], "\"");
continue;
}
}
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
} }
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
}
} }
std::string Authenticator::quote(std::string src) std::string Authenticator::quote(std::string src)
{ {
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\""); return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
} }
std::string Authenticator::getAuthHeader(std::string method, std::string uri) std::string Authenticator::getAuthHeader(std::string method, std::string uri)
{ {
std::string result = "Authorization: "; std::string result = "Authorization: ";
if (fAuthMethod == AUTH_BASIC) if (fAuthMethod == AUTH_BASIC)
{ {
result += "Basic " + base64Encode( username() + ":" + password() ); result += "Basic " + base64Encode( username() + ":" + password() );
}
else if (fAuthMethod == AUTH_DIGEST)
{
result += std::string("Digest ") +
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
if ( ! fQop.empty() ) {
result += ", qop=" + fQop;
result += ", nc=" + stringtf("%08x",nc);
result += ", cnonce=\"" + fCnonce + "\"";
} }
else if (fAuthMethod == AUTH_DIGEST) result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
{ result += ", algorithm=\"MD5\"";
result += std::string("Digest ") +
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " + //Authorization: Digest username="zm",
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\""; // realm="NC-336PW-HD-1080P",
if ( ! fQop.empty() ) { // nonce="de8859d97609a6fcc16eaba490dcfd80",
result += ", qop=" + fQop; // uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
result += ", nc=" + stringtf("%08x",nc); // response="4092120557d3099a163bd51a0d59744d",
result += ", cnonce=\"" + fCnonce + "\""; // algorithm=MD5,
} // opaque="5ccc069c403ebaf9f0171e9517f40e41",
result += ", response=\"" + computeDigestResponse(method, uri) + "\""; // qop="auth",
result += ", algorithm=\"MD5\""; // cnonce="c8051140765877dc",
// nc=00000001
//Authorization: Digest username="zm",
// realm="NC-336PW-HD-1080P", }
// nonce="de8859d97609a6fcc16eaba490dcfd80", result += "\r\n";
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp", return result;
// response="4092120557d3099a163bd51a0d59744d",
// algorithm=MD5,
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
// qop="auth",
// cnonce="c8051140765877dc",
// nc=00000001
}
result += "\r\n";
return result;
} }
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) { std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
// The "response" field is computed as: // The "response" field is computed as:
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>)) // md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
size_t md5len = 16; size_t md5len = 16;
unsigned char md5buf[md5len]; unsigned char md5buf[md5len];
char md5HexBuf[md5len*2+1]; char md5HexBuf[md5len*2+1];
// Step 1: md5(<username>:<realm>:<password>) // Step 1: md5(<username>:<realm>:<password>)
std::string ha1Data = username() + ":" + realm() + ":" + password(); std::string ha1Data = username() + ":" + realm() + ":" + password();
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() ); Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
#if HAVE_DECL_MD5 #if HAVE_DECL_MD5
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT #elif HAVE_DECL_GNUTLS_FINGERPRINT
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
#endif #endif
for ( unsigned int j = 0; j < md5len; j++ ) for ( unsigned int j = 0; j < md5len; j++ )
{ {
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] ); sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
} }
md5HexBuf[md5len*2]='\0'; md5HexBuf[md5len*2]='\0';
std::string ha1Hash = md5HexBuf; std::string ha1Hash = md5HexBuf;
// Step 2: md5(<cmd>:<url>) // Step 2: md5(<cmd>:<url>)
std::string ha2Data = method + ":" + uri; std::string ha2Data = method + ":" + uri;
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() ); Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
#if HAVE_DECL_MD5 #if HAVE_DECL_MD5
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
#elif HAVE_DECL_GNUTLS_FINGERPRINT #elif HAVE_DECL_GNUTLS_FINGERPRINT
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
#endif #endif
for ( unsigned int j = 0; j < md5len; j++ ) for ( unsigned int j = 0; j < md5len; j++ )
{ {
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
} }
md5HexBuf[md5len*2]='\0'; md5HexBuf[md5len*2]='\0';
std::string ha2Hash = md5HexBuf; std::string ha2Hash = md5HexBuf;
// Step 3: md5(ha1:<nonce>:ha2) // Step 3: md5(ha1:<nonce>:ha2)
std::string digestData = ha1Hash + ":" + nonce(); std::string digestData = ha1Hash + ":" + nonce();
if ( ! fQop.empty() ) { if ( ! fQop.empty() ) {
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop; digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
nc ++; nc ++;
// if qop was specified, then we have to include t and a cnonce and an nccount // if qop was specified, then we have to include t and a cnonce and an nccount
} }
digestData += ":" + ha2Hash; digestData += ":" + ha2Hash;
Debug( 2, "pre-md5: %s", digestData.c_str() ); Debug( 2, "pre-md5: %s", digestData.c_str() );
#if HAVE_DECL_MD5 #if HAVE_DECL_MD5
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
#elif HAVE_DECL_GNUTLS_FINGERPRINT #elif HAVE_DECL_GNUTLS_FINGERPRINT
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
#endif #endif
for ( unsigned int j = 0; j < md5len; j++ ) for ( unsigned int j = 0; j < md5len; j++ )
{ {
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
} }
md5HexBuf[md5len*2]='\0'; md5HexBuf[md5len*2]='\0';
return md5HexBuf; return md5HexBuf;
#else // HAVE_DECL_MD5 #else // HAVE_DECL_MD5
Error( "You need to build with gnutls or openssl installed to use digest authentication" ); Error( "You need to build with gnutls or openssl installed to use digest authentication" );
return( 0 ); return( 0 );
#endif // HAVE_DECL_MD5 #endif // HAVE_DECL_MD5
} }
void Authenticator::checkAuthResponse(std::string &response) { void Authenticator::checkAuthResponse(std::string &response) {
std::string authLine; std::string authLine;
StringVector lines = split( response, "\r\n" ); StringVector lines = split( response, "\r\n" );
const char* authenticate_match = "WWW-Authenticate:"; const char* authenticate_match = "WWW-Authenticate:";
size_t authenticate_match_len = strlen(authenticate_match); size_t authenticate_match_len = strlen(authenticate_match);
for ( size_t i = 0; i < lines.size(); i++ ) { for ( size_t i = 0; i < lines.size(); i++ ) {
// stop at end of headers // stop at end of headers
if (lines[i].length()==0) if (lines[i].length()==0)
break; break;
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) { if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
authLine = lines[i]; authLine = lines[i];
Debug( 2, "Found auth line at %d", i); Debug( 2, "Found auth line at %d", i);
break; break;
} }
} }
if (!authLine.empty()) { if (!authLine.empty()) {
Debug( 2, "Analyze auth line %s", authLine.c_str()); Debug( 2, "Analyze auth line %s", authLine.c_str());
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) ); authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
} else { } else {
Debug( 2, "Didn't find auth line in %s", authLine.c_str()); Debug( 2, "Didn't find auth line in %s", authLine.c_str());
} }
} }
} // namespace zm } // namespace zm

View File

@ -37,20 +37,20 @@ namespace zm {
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 }; enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
class Authenticator { class Authenticator {
public: public:
Authenticator(std::string &username, std::string password); Authenticator(std::string &username, std::string password);
virtual ~Authenticator(); virtual ~Authenticator();
void reset(); void reset();
std::string realm() { return fRealm; } std::string realm() { return fRealm; }
std::string nonce() { return fNonce; } std::string nonce() { return fNonce; }
std::string username() { return fUsername; } std::string username() { return fUsername; }
AuthMethod auth_method() const { return fAuthMethod; } AuthMethod auth_method() const { return fAuthMethod; }
std::string computeDigestResponse( std::string &cmd, std::string &url ); std::string computeDigestResponse( std::string &cmd, std::string &url );
void authHandleHeader( std::string headerData ); void authHandleHeader( std::string headerData );
std::string getAuthHeader( std::string method, std::string path ); std::string getAuthHeader( std::string method, std::string path );
void checkAuthResponse(std::string &response); void checkAuthResponse(std::string &response);
private: private:
std::string password() { return fPassword; } std::string password() { return fPassword; }
AuthMethod fAuthMethod; AuthMethod fAuthMethod;
@ -61,7 +61,7 @@ private:
std::string fUsername; std::string fUsername;
std::string fPassword; std::string fPassword;
std::string quote( std::string src ); std::string quote( std::string src );
int nc; int nc;
}; };
} // namespace zm } // namespace zm

View File

@ -25,489 +25,489 @@
#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))
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 }, { 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 }, { 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
}; };
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
}; };
#else #else
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 }, { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 }, { 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 }, { 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 }, { 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 }, { 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 } { -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
}; };
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, { "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC }, { "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 }, { "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB }, { "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE } { "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
}; };
#endif #endif
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) : SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
mTtl( 16 ), mTtl( 16 ),
mNoAddresses( 0 ) mNoAddresses( 0 )
{ {
StringVector tokens = split( connInfo, " " ); StringVector tokens = split( connInfo, " " );
if ( tokens.size() < 3 ) if ( tokens.size() < 3 )
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
mNetworkType = tokens[0]; mNetworkType = tokens[0];
if ( mNetworkType != "IN" ) if ( mNetworkType != "IN" )
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
mAddressType = tokens[1]; mAddressType = tokens[1];
if ( mAddressType != "IP4" ) if ( mAddressType != "IP4" )
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
StringVector addressTokens = split( tokens[2], "/" ); StringVector addressTokens = split( tokens[2], "/" );
if ( addressTokens.size() < 1 ) if ( addressTokens.size() < 1 )
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" ); throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
mAddress = addressTokens[0]; mAddress = addressTokens[0];
if ( addressTokens.size() >= 2 ) if ( addressTokens.size() >= 2 )
mTtl = atoi(addressTokens[1].c_str()); mTtl = atoi(addressTokens[1].c_str());
if ( addressTokens.size() >= 3 ) if ( addressTokens.size() >= 3 )
mNoAddresses = atoi(addressTokens[2].c_str()); mNoAddresses = atoi(addressTokens[2].c_str());
} }
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
mValue( 0 ) mValue( 0 )
{ {
StringVector tokens = split( bandInfo, ":" ); StringVector tokens = split( bandInfo, ":" );
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" ); throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
mType = tokens[0]; mType = tokens[0];
//if ( mNetworkType != "IN" ) //if ( mNetworkType != "IN" )
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); //throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
mValue = atoi(tokens[1].c_str()); mValue = atoi(tokens[1].c_str());
} }
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) : SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
mType( type ), mType( type ),
mPort( port ), mPort( port ),
mNumPorts( numPorts ), mNumPorts( numPorts ),
mTransport( transport ), mTransport( transport ),
mPayloadType( payloadType ), mPayloadType( payloadType ),
mFrameRate( 0.0 ), mFrameRate( 0.0 ),
mClock( 0 ), mClock( 0 ),
mWidth( 0 ), mWidth( 0 ),
mHeight( 0 ), mHeight( 0 ),
mSprops( "" ), mSprops( "" ),
mConnInfo( 0 ) mConnInfo( 0 )
{ {
} }
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) : SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
mUrl( url ), mUrl( url ),
mConnInfo( 0 ), mConnInfo( 0 ),
mBandInfo( 0 ) mBandInfo( 0 )
{ {
MediaDescriptor *currMedia = 0; MediaDescriptor *currMedia = 0;
StringVector lines = split( sdp, "\r\n" ); StringVector lines = split( sdp, "\r\n" );
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ ) for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
{
std::string line = *iter;
if ( line.empty() )
break;
Debug( 3, "Processing SDP line '%s'", line.c_str() );
const char sdpType = line[0];
if ( line[1] != '=' )
throw Exception( "Invalid SDP format at '"+line+"'" );
line.erase( 0, 2 );
switch( sdpType )
{ {
std::string line = *iter; case 'v' :
if ( line.empty() ) mVersion = line;
break; break;
case 'o' :
Debug( 3, "Processing SDP line '%s'", line.c_str() ); mOwner = line;
const char sdpType = line[0]; break;
if ( line[1] != '=' ) case 's' :
throw Exception( "Invalid SDP format at '"+line+"'" ); mName = line;
break;
line.erase( 0, 2 ); case 'i' :
switch( sdpType ) mInfo = line;
break;
case 'c' :
// This prevent a memory leak if the field appears more than one time
if ( mConnInfo )
delete mConnInfo;
mConnInfo = new ConnInfo( line );
break;
case 'b' :
// This prevent a memory leak if the field appears more than one time
if ( mBandInfo )
delete mBandInfo;
mBandInfo = new BandInfo( line );
break;
case 't' :
mTimeInfo = line;
break;
case 'a' :
{
mAttributes.push_back( line );
StringVector tokens = split( line, ":", 2 );
std::string attrName = tokens[0];
if ( currMedia )
{ {
case 'v' : if ( attrName == "control" )
mVersion = line; {
break; if ( tokens.size() < 2 )
case 'o' : throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
mOwner = line; currMedia->setControlUrl( tokens[1] );
break; }
case 's' : else if ( attrName == "range" )
mName = line; {
break; }
case 'i' : else if ( attrName == "rtpmap" )
mInfo = line; {
break; // a=rtpmap:96 MP4V-ES/90000
case 'c' : if ( tokens.size() < 2 )
// This prevent a memory leak if the field appears more than one time throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
if ( mConnInfo ) StringVector attrTokens = split( tokens[1], " " );
delete mConnInfo; int payloadType = atoi(attrTokens[0].c_str());
mConnInfo = new ConnInfo( line ); if ( payloadType != currMedia->getPayloadType() )
break; throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
case 'b' : std::string payloadDesc = attrTokens[1];
// This prevent a memory leak if the field appears more than one time //currMedia->setPayloadType( payloadType );
if ( mBandInfo ) if ( attrTokens.size() > 1 )
delete mBandInfo;
mBandInfo = new BandInfo( line );
break;
case 't' :
mTimeInfo = line;
break;
case 'a' :
{ {
mAttributes.push_back( line ); StringVector payloadTokens = split( attrTokens[1], "/" );
StringVector tokens = split( line, ":", 2 ); std::string payloadDesc = payloadTokens[0];
std::string attrName = tokens[0]; int payloadClock = atoi(payloadTokens[1].c_str());
if ( currMedia ) currMedia->setPayloadDesc( payloadDesc );
{ currMedia->setClock( payloadClock );
if ( attrName == "control" )
{
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
currMedia->setControlUrl( tokens[1] );
}
else if ( attrName == "range" )
{
}
else if ( attrName == "rtpmap" )
{
// a=rtpmap:96 MP4V-ES/90000
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " " );
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
std::string payloadDesc = attrTokens[1];
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{
StringVector payloadTokens = split( attrTokens[1], "/" );
std::string payloadDesc = payloadTokens[0];
int payloadClock = atoi(payloadTokens[1].c_str());
currMedia->setPayloadDesc( payloadDesc );
currMedia->setClock( payloadClock );
}
}
else if ( attrName == "framesize" )
{
// a=framesize:96 320-240
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " " );
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
//currMedia->setPayloadType( payloadType );
StringVector sizeTokens = split( attrTokens[1], "-" );
int width = atoi(sizeTokens[0].c_str());
int height = atoi(sizeTokens[1].c_str());
currMedia->setFrameSize( width, height );
}
else if ( attrName == "framerate" )
{
// a=framerate:5.0
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
double frameRate = atof(tokens[1].c_str());
currMedia->setFrameRate( frameRate );
}
else if ( attrName == "fmtp" )
{
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " ", 2 );
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{
StringVector attr2Tokens = split( attrTokens[1], "; " );
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
{
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
if ( attr3Tokens[0] == "profile-level-id" )
{
}
else if ( attr3Tokens[0] == "config" )
{
}
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
{
size_t t = attr2Tokens[i].find("=");
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
Debug(4, "sprop-parameter-sets value %s", c);
currMedia->setSprops(std::string(c));
}
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
{
size_t t = attr2Tokens[i].find("=");
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
Debug(4, "sprop-parameter-sets value %s", c);
currMedia->setSprops(std::string(c));
}
else
{
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
}
}
}
}
else if ( attrName == "mpeg4-iod" )
{
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
}
else if ( attrName == "mpeg4-esid" )
{
// a=mpeg4-esid:201
}
else
{
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
}
}
else
{
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
}
break;
} }
case 'm' : }
else if ( attrName == "framesize" )
{
// a=framesize:96 320-240
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " " );
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
//currMedia->setPayloadType( payloadType );
StringVector sizeTokens = split( attrTokens[1], "-" );
int width = atoi(sizeTokens[0].c_str());
int height = atoi(sizeTokens[1].c_str());
currMedia->setFrameSize( width, height );
}
else if ( attrName == "framerate" )
{
// a=framerate:5.0
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
double frameRate = atof(tokens[1].c_str());
currMedia->setFrameRate( frameRate );
}
else if ( attrName == "fmtp" )
{
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
StringVector attrTokens = split( tokens[1], " ", 2 );
int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() )
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{ {
StringVector tokens = split( line, " " ); StringVector attr2Tokens = split( attrTokens[1], "; " );
if ( tokens.size() < 4 ) for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
throw Exception( "Can't parse SDP media description '"+line+"'" ); {
std::string mediaType = tokens[0]; StringVector attr3Tokens = split( attr2Tokens[i], "=" );
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); if ( attr3Tokens[0] == "profile-level-id" )
StringVector portTokens = split( tokens[1], "/" ); {
int mediaPort = atoi(portTokens[0].c_str()); }
int mediaNumPorts = 1; else if ( attr3Tokens[0] == "config" )
if ( portTokens.size() > 1 ) {
mediaNumPorts = atoi(portTokens[1].c_str()); }
std::string mediaTransport = tokens[2]; else if ( attr3Tokens[0] == "sprop-parameter-sets" )
if ( mediaTransport != "RTP/AVP" ) {
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); size_t t = attr2Tokens[i].find("=");
int payloadType = atoi(tokens[3].c_str()); char *c = (char *)attr2Tokens[i].c_str() + t + 1;
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); Debug(4, "sprop-parameter-sets value %s", c);
mMediaList.push_back( currMedia ); currMedia->setSprops(std::string(c));
break; }
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
{
size_t t = attr2Tokens[i].find("=");
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
Debug(4, "sprop-parameter-sets value %s", c);
currMedia->setSprops(std::string(c));
}
else
{
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
}
}
} }
}
else if ( attrName == "mpeg4-iod" )
{
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
}
else if ( attrName == "mpeg4-esid" )
{
// a=mpeg4-esid:201
}
else
{
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
}
} }
else
{
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
}
break;
}
case 'm' :
{
StringVector tokens = split( line, " " );
if ( tokens.size() < 4 )
throw Exception( "Can't parse SDP media description '"+line+"'" );
std::string mediaType = tokens[0];
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
StringVector portTokens = split( tokens[1], "/" );
int mediaPort = atoi(portTokens[0].c_str());
int mediaNumPorts = 1;
if ( portTokens.size() > 1 )
mediaNumPorts = atoi(portTokens[1].c_str());
std::string mediaTransport = tokens[2];
if ( mediaTransport != "RTP/AVP" )
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
int payloadType = atoi(tokens[3].c_str());
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
mMediaList.push_back( currMedia );
break;
}
} }
}
} }
SessionDescriptor::~SessionDescriptor() SessionDescriptor::~SessionDescriptor()
{ {
if ( mConnInfo ) if ( mConnInfo )
delete mConnInfo; delete mConnInfo;
if ( mBandInfo ) if ( mBandInfo )
delete mBandInfo; delete mBandInfo;
for ( unsigned int i = 0; i < mMediaList.size(); i++ ) for ( unsigned int i = 0; i < mMediaList.size(); i++ )
delete mMediaList[i]; delete mMediaList[i];
} }
AVFormatContext *SessionDescriptor::generateFormatContext() const AVFormatContext *SessionDescriptor::generateFormatContext() const
{ {
AVFormatContext *formatContext = avformat_alloc_context(); AVFormatContext *formatContext = avformat_alloc_context();
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) ); strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
/* /*
if ( mName.length() ) if ( mName.length() )
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) ); strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
if ( mInfo.length() ) if ( mInfo.length() )
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) ); strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
*/ */
//formatContext->nb_streams = mMediaList.size(); //formatContext->nb_streams = mMediaList.size();
for ( unsigned int i = 0; i < mMediaList.size(); i++ ) for ( unsigned int i = 0; i < mMediaList.size(); i++ )
{ {
const MediaDescriptor *mediaDesc = mMediaList[i]; const MediaDescriptor *mediaDesc = mMediaList[i];
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
AVStream *stream = av_new_stream( formatContext, i ); AVStream *stream = av_new_stream( formatContext, i );
#else #else
AVStream *stream = avformat_new_stream( formatContext, NULL ); AVStream *stream = avformat_new_stream( formatContext, NULL );
stream->id = i; stream->id = i;
#endif #endif
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mediaDesc->getType() == "video" ) if ( mediaDesc->getType() == "video" )
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" ) else if ( mediaDesc->getType() == "audio" )
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" ) else if ( mediaDesc->getType() == "application" )
stream->codec->codec_type = AVMEDIA_TYPE_DATA; stream->codec->codec_type = AVMEDIA_TYPE_DATA;
#else #else
if ( mediaDesc->getType() == "video" ) if ( mediaDesc->getType() == "video" )
stream->codec->codec_type = CODEC_TYPE_VIDEO; stream->codec->codec_type = CODEC_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" ) else if ( mediaDesc->getType() == "audio" )
stream->codec->codec_type = CODEC_TYPE_AUDIO; stream->codec->codec_type = CODEC_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" ) else if ( mediaDesc->getType() == "application" )
stream->codec->codec_type = CODEC_TYPE_DATA; stream->codec->codec_type = CODEC_TYPE_DATA;
#endif #endif
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
std::string codec_name; std::string codec_name;
#endif #endif
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
{
// Look in static table
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
{
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
{ {
// Look in static table Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
{
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
{
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string( smStaticPayloads[i].payloadName );
#else #else
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
#endif #endif
stream->codec->codec_type = smStaticPayloads[i].codecType; stream->codec->codec_type = smStaticPayloads[i].codecType;
stream->codec->codec_id = smStaticPayloads[i].codecId; stream->codec->codec_id = smStaticPayloads[i].codecId;
stream->codec->sample_rate = smStaticPayloads[i].clockRate; stream->codec->sample_rate = smStaticPayloads[i].clockRate;
break; break;
}
}
} }
else }
}
else
{
// Look in dynamic table
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
{
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
{ {
// Look in dynamic table Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
{
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
{
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string( smStaticPayloads[i].payloadName );
#else #else
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
#endif #endif
stream->codec->codec_type = smDynamicPayloads[i].codecType; stream->codec->codec_type = smDynamicPayloads[i].codecType;
stream->codec->codec_id = smDynamicPayloads[i].codecId; stream->codec->codec_id = smDynamicPayloads[i].codecId;
stream->codec->sample_rate = mediaDesc->getClock(); stream->codec->sample_rate = mediaDesc->getClock();
break; break;
}
}
}
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
if ( codec_name.empty() )
#else
if ( !stream->codec->codec_name[0] )
#endif
{
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
//return( 0 );
}
if ( mediaDesc->getWidth() )
stream->codec->width = mediaDesc->getWidth();
if ( mediaDesc->getHeight() )
stream->codec->height = mediaDesc->getHeight();
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
{
uint8_t start_sequence[]= { 0, 0, 1 };
stream->codec->extradata_size= 0;
stream->codec->extradata= NULL;
char pvalue[1024], *value = pvalue;
strcpy(pvalue, mediaDesc->getSprops().c_str());
while (*value) {
char base64packet[1024];
uint8_t decoded_packet[1024];
uint32_t packet_size;
char *dst = base64packet;
while (*value && *value != ','
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
*dst++ = *value++;
}
*dst++ = '\0';
if (*value == ',')
value++;
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
Hexdump(4, (char *)decoded_packet, packet_size);
if (packet_size) {
uint8_t *dest =
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
stream->codec->extradata_size +
FF_INPUT_BUFFER_PADDING_SIZE);
if(dest) {
if(stream->codec->extradata_size) {
// av_realloc?
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
av_free(stream->codec->extradata);
}
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
stream->codec->extradata= dest;
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
// } else {
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
// return AVERROR(ENOMEM);
}
}
}
} }
}
} }
return( formatContext ); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
if ( codec_name.empty() )
#else
if ( !stream->codec->codec_name[0] )
#endif
{
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
//return( 0 );
}
if ( mediaDesc->getWidth() )
stream->codec->width = mediaDesc->getWidth();
if ( mediaDesc->getHeight() )
stream->codec->height = mediaDesc->getHeight();
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
{
uint8_t start_sequence[]= { 0, 0, 1 };
stream->codec->extradata_size= 0;
stream->codec->extradata= NULL;
char pvalue[1024], *value = pvalue;
strcpy(pvalue, mediaDesc->getSprops().c_str());
while (*value) {
char base64packet[1024];
uint8_t decoded_packet[1024];
uint32_t packet_size;
char *dst = base64packet;
while (*value && *value != ','
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
*dst++ = *value++;
}
*dst++ = '\0';
if (*value == ',')
value++;
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
Hexdump(4, (char *)decoded_packet, packet_size);
if (packet_size) {
uint8_t *dest =
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
stream->codec->extradata_size +
FF_INPUT_BUFFER_PADDING_SIZE);
if(dest) {
if(stream->codec->extradata_size) {
// av_realloc?
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
av_free(stream->codec->extradata);
}
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
stream->codec->extradata= dest;
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
// } else {
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
// return AVERROR(ENOMEM);
}
}
}
}
}
return( formatContext );
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -34,204 +34,204 @@
class SessionDescriptor class SessionDescriptor
{ {
protected: protected:
enum { PAYLOAD_TYPE_DYNAMIC=96 }; enum { PAYLOAD_TYPE_DYNAMIC=96 };
struct StaticPayloadDesc struct StaticPayloadDesc
{ {
int payloadType; int payloadType;
const char payloadName[6]; const char payloadName[6];
#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))
AVMediaType codecType; AVMediaType codecType;
#else #else
enum CodecType codecType; enum CodecType codecType;
#endif #endif
_AVCODECID codecId; _AVCODECID codecId;
int clockRate; int clockRate;
int autoChannels; int autoChannels;
}; };
struct DynamicPayloadDesc struct DynamicPayloadDesc
{ {
const char payloadName[32]; const char payloadName[32];
#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))
AVMediaType codecType; AVMediaType codecType;
#else #else
enum CodecType codecType; enum CodecType codecType;
#endif #endif
_AVCODECID codecId; _AVCODECID codecId;
//int clockRate; //int clockRate;
//int autoChannels; //int autoChannels;
}; };
public: public:
class ConnInfo class ConnInfo
{ {
protected: protected:
std::string mNetworkType; std::string mNetworkType;
std::string mAddressType; std::string mAddressType;
std::string mAddress; std::string mAddress;
int mTtl; int mTtl;
int mNoAddresses; int mNoAddresses;
public: public:
ConnInfo( const std::string &connInfo ); ConnInfo( const std::string &connInfo );
}; };
class BandInfo class BandInfo
{ {
protected: protected:
std::string mType; std::string mType;
int mValue; int mValue;
public: public:
BandInfo( const std::string &bandInfo ); BandInfo( const std::string &bandInfo );
}; };
class MediaDescriptor class MediaDescriptor
{ {
protected: protected:
std::string mType; std::string mType;
int mPort; int mPort;
int mNumPorts; int mNumPorts;
std::string mTransport; std::string mTransport;
int mPayloadType; int mPayloadType;
std::string mPayloadDesc; std::string mPayloadDesc;
std::string mControlUrl; std::string mControlUrl;
double mFrameRate; double mFrameRate;
int mClock; int mClock;
int mWidth; int mWidth;
int mHeight; int mHeight;
std::string mSprops; std::string mSprops;
ConnInfo *mConnInfo;
public:
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
const std::string &getType() const
{
return( mType );
}
int getPort() const
{
return( mPort );
}
int getNumPorts() const
{
return( mNumPorts );
}
const std::string &getTransport() const
{
return( mTransport );
}
const int getPayloadType() const
{
return( mPayloadType );
}
const std::string &getPayloadDesc() const
{
return( mPayloadDesc );
}
void setPayloadDesc( const std::string &payloadDesc )
{
mPayloadDesc = payloadDesc;
}
const std::string &getControlUrl() const
{
return( mControlUrl );
}
void setControlUrl( const std::string &controlUrl )
{
mControlUrl = controlUrl;
}
const int getClock() const
{
return( mClock );
}
void setClock( int clock )
{
mClock = clock;
}
void setFrameSize( int width, int height )
{
mWidth = width;
mHeight = height;
}
int getWidth() const
{
return( mWidth );
}
int getHeight() const
{
return( mHeight );
}
void setSprops(const std::string props)
{
mSprops = props;
}
const std::string getSprops() const
{
return ( mSprops );
}
const double getFrameRate() const
{
return( mFrameRate );
}
void setFrameRate( double frameRate )
{
mFrameRate = frameRate;
}
};
typedef std::vector<MediaDescriptor *> MediaList;
protected:
static StaticPayloadDesc smStaticPayloads[];
static DynamicPayloadDesc smDynamicPayloads[];
protected:
std::string mUrl;
std::string mVersion;
std::string mOwner;
std::string mName;
std::string mInfo;
ConnInfo *mConnInfo; ConnInfo *mConnInfo;
BandInfo *mBandInfo;
std::string mTimeInfo;
StringVector mAttributes;
MediaList mMediaList; public:
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
const std::string &getType() const
{
return( mType );
}
int getPort() const
{
return( mPort );
}
int getNumPorts() const
{
return( mNumPorts );
}
const std::string &getTransport() const
{
return( mTransport );
}
const int getPayloadType() const
{
return( mPayloadType );
}
const std::string &getPayloadDesc() const
{
return( mPayloadDesc );
}
void setPayloadDesc( const std::string &payloadDesc )
{
mPayloadDesc = payloadDesc;
}
const std::string &getControlUrl() const
{
return( mControlUrl );
}
void setControlUrl( const std::string &controlUrl )
{
mControlUrl = controlUrl;
}
const int getClock() const
{
return( mClock );
}
void setClock( int clock )
{
mClock = clock;
}
void setFrameSize( int width, int height )
{
mWidth = width;
mHeight = height;
}
int getWidth() const
{
return( mWidth );
}
int getHeight() const
{
return( mHeight );
}
void setSprops(const std::string props)
{
mSprops = props;
}
const std::string getSprops() const
{
return ( mSprops );
}
const double getFrameRate() const
{
return( mFrameRate );
}
void setFrameRate( double frameRate )
{
mFrameRate = frameRate;
}
};
typedef std::vector<MediaDescriptor *> MediaList;
protected:
static StaticPayloadDesc smStaticPayloads[];
static DynamicPayloadDesc smDynamicPayloads[];
protected:
std::string mUrl;
std::string mVersion;
std::string mOwner;
std::string mName;
std::string mInfo;
ConnInfo *mConnInfo;
BandInfo *mBandInfo;
std::string mTimeInfo;
StringVector mAttributes;
MediaList mMediaList;
public: public:
SessionDescriptor( const std::string &url, const std::string &sdp ); SessionDescriptor( const std::string &url, const std::string &sdp );
~SessionDescriptor(); ~SessionDescriptor();
const std::string &getUrl() const const std::string &getUrl() const
{ {
return( mUrl ); return( mUrl );
} }
int getNumStreams() const int getNumStreams() const
{ {
return( mMediaList.size() ); return( mMediaList.size() );
} }
MediaDescriptor *getStream( int index ) MediaDescriptor *getStream( int index )
{ {
if ( index < 0 || (unsigned int)index >= mMediaList.size() ) if ( index < 0 || (unsigned int)index >= mMediaList.size() )
return( 0 ); return( 0 );
return( mMediaList[index] ); return( mMediaList[index] );
} }
AVFormatContext *generateFormatContext() const; AVFormatContext *generateFormatContext() const;
}; };
#if 0 #if 0
v=0 v=0
@ -254,7 +254,7 @@ a=mpeg4-esid:201
m=audio 0 RTP/AVP 0 m=audio 0 RTP/AVP 0
b=AS:64 b=AS:64
a=control:trackID=2 a=control:trackID=2
#endif #endif
#endif // ZM_SDP_H #endif // ZM_SDP_H

View File

@ -1,30 +1,30 @@
#ifdef HAVE_SENDFILE4_SUPPORT #ifdef HAVE_SENDFILE4_SUPPORT
#include <sys/sendfile.h> #include <sys/sendfile.h>
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
int err; int err;
err = sendfile(out_fd, in_fd, offset, size); err = sendfile(out_fd, in_fd, offset, size);
if (err < 0) if (err < 0)
return -errno; return -errno;
return err; return err;
} }
#elif HAVE_SENDFILE7_SUPPORT #elif HAVE_SENDFILE7_SUPPORT
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h> #include <sys/uio.h>
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) { int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
int err; int err;
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0); err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
if (err && errno != EAGAIN) if (err && errno != EAGAIN)
return -errno; return -errno;
if (size) { if (size) {
*offset += size; *offset += size;
return size; return size;
} }
return -EAGAIN; return -EAGAIN;
} }
#else #else
#error "Your platform does not support sendfile. Sorry." #error "Your platform does not support sendfile. Sorry."

View File

@ -32,326 +32,326 @@
StreamBase::~StreamBase() StreamBase::~StreamBase()
{ {
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
if ( vid_stream ) if ( vid_stream )
{ {
delete vid_stream; delete vid_stream;
vid_stream = NULL; vid_stream = NULL;
} }
#endif #endif
closeComms(); closeComms();
} }
bool StreamBase::loadMonitor( int monitor_id ) bool StreamBase::loadMonitor( int monitor_id )
{ {
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
{ {
Fatal( "Unable to load monitor id %d for streaming", monitor_id ); Fatal( "Unable to load monitor id %d for streaming", monitor_id );
return( false ); return( false );
} }
monitor->connect(); monitor->connect();
return( true ); return( true );
} }
bool StreamBase::checkInitialised() bool StreamBase::checkInitialised()
{ {
if ( !monitor ) if ( !monitor )
{ {
Fatal( "Cannot stream, not initialised" ); Fatal( "Cannot stream, not initialised" );
return( false ); return( false );
} }
return( true ); return( true );
} }
void StreamBase::updateFrameRate( double fps ) void StreamBase::updateFrameRate( double fps )
{ {
base_fps = fps; base_fps = fps;
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
frame_mod = 1; frame_mod = 1;
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
// Min frame repeat? // Min frame repeat?
while( effective_fps > maxfps ) while( effective_fps > maxfps )
{ {
effective_fps /= 2.0; effective_fps /= 2.0;
frame_mod *= 2; frame_mod *= 2;
} }
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
} }
bool StreamBase::checkCommandQueue() bool StreamBase::checkCommandQueue()
{ {
if ( sd >= 0 ) if ( sd >= 0 )
{
CmdMsg msg;
memset( &msg, 0, sizeof(msg) );
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
if ( nbytes < 0 )
{ {
CmdMsg msg; if ( errno != EAGAIN )
memset( &msg, 0, sizeof(msg) ); {
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
if ( nbytes < 0 ) }
{
if ( errno != EAGAIN )
{
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
}
}
//else if ( (nbytes != sizeof(msg)) )
//{
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
//}
else
{
processCommand( &msg );
return( true );
}
} }
return( false ); //else if ( (nbytes != sizeof(msg)) )
//{
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
//}
else
{
processCommand( &msg );
return( true );
}
}
return( false );
} }
Image *StreamBase::prepareImage( Image *image ) Image *StreamBase::prepareImage( Image *image )
{ {
static int last_scale = 0; static int last_scale = 0;
static int last_zoom = 0; static int last_zoom = 0;
static int last_x = 0; static int last_x = 0;
static int last_y = 0; static int last_y = 0;
if ( !last_scale ) if ( !last_scale )
last_scale = scale;
if ( !last_zoom )
last_zoom = zoom;
// Do not bother to scale zoomed in images, just crop them and let the browser scale
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
bool optimisedScaling = false;
bool image_copied = false;
int mag = (scale * zoom) / ZM_SCALE_BASE;
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
int base_image_width = image->Width(), base_image_height = image->Height();
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
if ( mag != ZM_SCALE_BASE )
{
if ( act_mag != ZM_SCALE_BASE )
{
Debug( 3, "Magnifying by %d", mag );
if ( !image_copied )
{
static Image copy_image;
copy_image.Assign( *image );
image = &copy_image;
image_copied = true;
}
image->Scale( mag );
}
}
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
{
static Box last_crop;
if ( mag != last_mag || x != last_x || y != last_y )
{
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
//if ( !last_mag )
//last_mag = mag;
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
last_crop = Box();
Debug( 3, "Recalculating crop" );
// Recalculate crop parameters, as %ges
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
click_x += ( x * 100 ) / last_virt_image_width;
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
click_y += ( y * 100 ) / last_virt_image_height;
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
// Convert the click locations to the current image pixels
click_x = ( click_x * act_image_width ) / 100;
click_y = ( click_y * act_image_height ) / 100;
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
int lo_x = click_x - (send_image_width/2);
if ( lo_x < 0 )
lo_x = 0;
int hi_x = lo_x + (send_image_width-1);
if ( hi_x >= act_image_width )
{
hi_x = act_image_width - 1;
lo_x = hi_x - (send_image_width - 1);
}
int lo_y = click_y - (send_image_height/2);
if ( lo_y < 0 )
lo_y = 0;
int hi_y = lo_y + (send_image_height-1);
if ( hi_y >= act_image_height )
{
hi_y = act_image_height - 1;
lo_y = hi_y - (send_image_height - 1);
}
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
}
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
if ( !image_copied )
{
static Image copy_image;
copy_image.Assign( *image );
image = &copy_image;
image_copied = true;
}
image->Crop( last_crop );
}
last_scale = scale; last_scale = scale;
if ( !last_zoom )
last_zoom = zoom; last_zoom = zoom;
last_x = x;
last_y = y;
return( image ); // Do not bother to scale zoomed in images, just crop them and let the browser scale
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
bool optimisedScaling = false;
bool image_copied = false;
int mag = (scale * zoom) / ZM_SCALE_BASE;
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
int base_image_width = image->Width(), base_image_height = image->Height();
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
if ( mag != ZM_SCALE_BASE )
{
if ( act_mag != ZM_SCALE_BASE )
{
Debug( 3, "Magnifying by %d", mag );
if ( !image_copied )
{
static Image copy_image;
copy_image.Assign( *image );
image = &copy_image;
image_copied = true;
}
image->Scale( mag );
}
}
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
{
static Box last_crop;
if ( mag != last_mag || x != last_x || y != last_y )
{
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
//if ( !last_mag )
//last_mag = mag;
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
last_crop = Box();
Debug( 3, "Recalculating crop" );
// Recalculate crop parameters, as %ges
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
click_x += ( x * 100 ) / last_virt_image_width;
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
click_y += ( y * 100 ) / last_virt_image_height;
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
// Convert the click locations to the current image pixels
click_x = ( click_x * act_image_width ) / 100;
click_y = ( click_y * act_image_height ) / 100;
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
int lo_x = click_x - (send_image_width/2);
if ( lo_x < 0 )
lo_x = 0;
int hi_x = lo_x + (send_image_width-1);
if ( hi_x >= act_image_width )
{
hi_x = act_image_width - 1;
lo_x = hi_x - (send_image_width - 1);
}
int lo_y = click_y - (send_image_height/2);
if ( lo_y < 0 )
lo_y = 0;
int hi_y = lo_y + (send_image_height-1);
if ( hi_y >= act_image_height )
{
hi_y = act_image_height - 1;
lo_y = hi_y - (send_image_height - 1);
}
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
}
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
if ( !image_copied )
{
static Image copy_image;
copy_image.Assign( *image );
image = &copy_image;
image_copied = true;
}
image->Crop( last_crop );
}
last_scale = scale;
last_zoom = zoom;
last_x = x;
last_y = y;
return( image );
} }
bool StreamBase::sendTextFrame( const char *frame_text ) bool StreamBase::sendTextFrame( const char *frame_text )
{ {
Debug( 2, "Sending text frame '%s'", frame_text ); Debug( 2, "Sending text frame '%s'", frame_text );
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
image.Annotate( frame_text, image.centreCoord( frame_text ) ); image.Annotate( frame_text, image.centreCoord( frame_text ) );
if ( scale != 100 ) if ( scale != 100 )
{ {
image.Scale( scale ); image.Scale( scale );
} }
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
if ( type == STREAM_MPEG ) if ( type == STREAM_MPEG )
{
if ( !vid_stream )
{ {
if ( !vid_stream ) vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
{ fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); vid_stream->OpenStream();
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
vid_stream->OpenStream();
}
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
} }
else /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
}
else
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC
{
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
int n_bytes = 0;
image.EncodeJpeg( buffer, &n_bytes );
fprintf( stdout, "--ZoneMinderFrame\r\n" );
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
{ {
static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; Error( "Unable to send stream text frame: %s", strerror(errno) );
int n_bytes = 0; return( false );
image.EncodeJpeg( buffer, &n_bytes );
fprintf( stdout, "--ZoneMinderFrame\r\n" );
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
{
Error( "Unable to send stream text frame: %s", strerror(errno) );
return( false );
}
fprintf( stdout, "\r\n\r\n" );
fflush( stdout );
} }
last_frame_sent = TV_2_FLOAT( now ); fprintf( stdout, "\r\n\r\n" );
return( true ); fflush( stdout );
}
last_frame_sent = TV_2_FLOAT( now );
return( true );
} }
void StreamBase::openComms() void StreamBase::openComms()
{ {
if ( connkey > 0 ) if ( connkey > 0 )
{
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 )
{ {
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey); lock_fd = 0;
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 )
{
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
lock_fd = 0;
}
else if ( flock(lock_fd, LOCK_EX) != 0 )
{
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
close(lock_fd);
lock_fd = 0;
}
else
{
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
}
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
if ( sd < 0 )
{
Fatal( "Can't create socket: %s", strerror(errno) );
}
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
unlink( loc_sock_path );
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
loc_addr.sun_family = AF_UNIX;
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
{
Fatal( "Can't bind: %s", strerror(errno) );
}
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
rem_addr.sun_family = AF_UNIX;
} }
else if ( flock(lock_fd, LOCK_EX) != 0 )
{
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
close(lock_fd);
lock_fd = 0;
}
else
{
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
}
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
if ( sd < 0 )
{
Fatal( "Can't create socket: %s", strerror(errno) );
}
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
unlink( loc_sock_path );
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
loc_addr.sun_family = AF_UNIX;
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
{
Fatal( "Can't bind: %s", strerror(errno) );
}
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
rem_addr.sun_family = AF_UNIX;
}
} }
void StreamBase::closeComms() void StreamBase::closeComms()
{ {
if ( connkey > 0 ) if ( connkey > 0 )
{
if ( sd >= 0 )
{ {
if ( sd >= 0 ) close( sd );
{ sd = -1;
close( sd );
sd = -1;
}
if ( loc_sock_path[0] )
{
unlink( loc_sock_path );
}
if (lock_fd > 0)
{
close(lock_fd); //close it rather than unlock it incase it got deleted.
unlink(sock_path_lock);
}
} }
if ( loc_sock_path[0] )
{
unlink( loc_sock_path );
}
if (lock_fd > 0)
{
close(lock_fd); //close it rather than unlock it incase it got deleted.
unlink(sock_path_lock);
}
}
} }

View File

@ -33,149 +33,149 @@ class Monitor;
class StreamBase class StreamBase
{ {
public: public:
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType; typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
protected: protected:
static const int MAX_STREAM_DELAY = 5; // Seconds static const int MAX_STREAM_DELAY = 5; // Seconds
static const StreamType DEFAULT_TYPE = STREAM_JPEG; static const StreamType DEFAULT_TYPE = STREAM_JPEG;
enum { DEFAULT_RATE=ZM_RATE_BASE }; enum { DEFAULT_RATE=ZM_RATE_BASE };
enum { DEFAULT_SCALE=ZM_SCALE_BASE }; enum { DEFAULT_SCALE=ZM_SCALE_BASE };
enum { DEFAULT_ZOOM=ZM_SCALE_BASE }; enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
enum { DEFAULT_MAXFPS=10 }; enum { DEFAULT_MAXFPS=10 };
enum { DEFAULT_BITRATE=100000 }; enum { DEFAULT_BITRATE=100000 };
protected: protected:
typedef struct { typedef struct {
int msg_type; int msg_type;
char msg_data[16]; char msg_data[16];
} CmdMsg; } CmdMsg;
typedef struct { typedef struct {
int msg_type; int msg_type;
char msg_data[256]; char msg_data[256];
} DataMsg; } DataMsg;
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType; typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand; typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
protected: protected:
Monitor *monitor; Monitor *monitor;
StreamType type; StreamType type;
const char *format; const char *format;
int replay_rate; int replay_rate;
int scale; int scale;
int zoom; int zoom;
double maxfps; double maxfps;
int bitrate; int bitrate;
unsigned short x, y; unsigned short x, y;
protected: protected:
int connkey; int connkey;
int sd; int sd;
char loc_sock_path[PATH_MAX]; char loc_sock_path[PATH_MAX];
struct sockaddr_un loc_addr; struct sockaddr_un loc_addr;
char rem_sock_path[PATH_MAX]; char rem_sock_path[PATH_MAX];
struct sockaddr_un rem_addr; struct sockaddr_un rem_addr;
char sock_path_lock[PATH_MAX]; char sock_path_lock[PATH_MAX];
int lock_fd; int lock_fd;
protected: protected:
bool paused; bool paused;
int step; int step;
struct timeval now; struct timeval now;
double base_fps; double base_fps;
double effective_fps; double effective_fps;
int frame_mod; int frame_mod;
double last_frame_sent; double last_frame_sent;
struct timeval last_frame_timestamp; struct timeval last_frame_timestamp;
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
VideoStream *vid_stream; VideoStream *vid_stream;
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC
CmdMsg msg; CmdMsg msg;
protected: protected:
bool loadMonitor( int monitor_id ); bool loadMonitor( int monitor_id );
bool checkInitialised(); bool checkInitialised();
void updateFrameRate( double fps ); void updateFrameRate( double fps );
Image *prepareImage( Image *image ); Image *prepareImage( Image *image );
bool sendTextFrame( const char *text ); bool sendTextFrame( const char *text );
bool checkCommandQueue(); bool checkCommandQueue();
virtual void processCommand( const CmdMsg *msg )=0; virtual void processCommand( const CmdMsg *msg )=0;
public: public:
StreamBase() StreamBase()
{ {
monitor = 0; monitor = 0;
type = DEFAULT_TYPE; type = DEFAULT_TYPE;
format = ""; format = "";
replay_rate = DEFAULT_RATE; replay_rate = DEFAULT_RATE;
scale = DEFAULT_SCALE; scale = DEFAULT_SCALE;
zoom = DEFAULT_ZOOM; zoom = DEFAULT_ZOOM;
maxfps = DEFAULT_MAXFPS; maxfps = DEFAULT_MAXFPS;
bitrate = DEFAULT_BITRATE; bitrate = DEFAULT_BITRATE;
paused = false; paused = false;
step = 0; step = 0;
x = 0; x = 0;
y = 0; y = 0;
connkey = 0; connkey = 0;
sd = -1; sd = -1;
lock_fd = 0; lock_fd = 0;
memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
memset( &loc_addr, 0, sizeof(loc_addr) ); memset( &loc_addr, 0, sizeof(loc_addr) );
memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
memset( &rem_addr, 0, sizeof(rem_addr) ); memset( &rem_addr, 0, sizeof(rem_addr) );
base_fps = 0.0; base_fps = 0.0;
effective_fps = 0.0; effective_fps = 0.0;
frame_mod = 1; frame_mod = 1;
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
vid_stream = 0; vid_stream = 0;
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC
} }
virtual ~StreamBase(); virtual ~StreamBase();
void setStreamType( StreamType p_type ) void setStreamType( StreamType p_type )
{ {
type = p_type; type = p_type;
} }
void setStreamFormat( const char *p_format ) void setStreamFormat( const char *p_format )
{ {
format = p_format; format = p_format;
} }
void setStreamScale( int p_scale ) void setStreamScale( int p_scale )
{ {
scale = p_scale; scale = p_scale;
} }
void setStreamReplayRate( int p_rate ) void setStreamReplayRate( int p_rate )
{ {
replay_rate = p_rate; replay_rate = p_rate;
} }
void setStreamMaxFPS( double p_maxfps ) void setStreamMaxFPS( double p_maxfps )
{ {
maxfps = p_maxfps; maxfps = p_maxfps;
} }
void setStreamBitrate( int p_bitrate ) void setStreamBitrate( int p_bitrate )
{ {
bitrate = p_bitrate; bitrate = p_bitrate;
} }
void setStreamQueue( int p_connkey ) void setStreamQueue( int p_connkey )
{ {
connkey = p_connkey; connkey = p_connkey;
} }
virtual void openComms(); virtual void openComms();
virtual void closeComms(); virtual void closeComms();
virtual void runStream()=0; virtual void runStream()=0;
}; };
#endif // ZM_STREAM_H #endif // ZM_STREAM_H

View File

@ -29,310 +29,310 @@
struct timespec getTimeout( int secs ) struct timespec getTimeout( int secs )
{ {
struct timespec timeout; struct timespec timeout;
struct timeval temp_timeout; struct timeval temp_timeout;
gettimeofday( &temp_timeout, 0 ); gettimeofday( &temp_timeout, 0 );
timeout.tv_sec = temp_timeout.tv_sec + secs; timeout.tv_sec = temp_timeout.tv_sec + secs;
timeout.tv_nsec = temp_timeout.tv_usec*1000; timeout.tv_nsec = temp_timeout.tv_usec*1000;
return( timeout ); return( timeout );
} }
struct timespec getTimeout( double secs ) struct timespec getTimeout( double secs )
{ {
struct timespec timeout; struct timespec timeout;
struct timeval temp_timeout; struct timeval temp_timeout;
gettimeofday( &temp_timeout, 0 ); gettimeofday( &temp_timeout, 0 );
timeout.tv_sec = temp_timeout.tv_sec + int(secs); timeout.tv_sec = temp_timeout.tv_sec + int(secs);
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs))); timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
if ( timeout.tv_nsec > 1000000000 ) if ( timeout.tv_nsec > 1000000000 )
{ {
timeout.tv_sec += 1; timeout.tv_sec += 1;
timeout.tv_nsec -= 1000000000; timeout.tv_nsec -= 1000000000;
} }
return( timeout ); return( timeout );
} }
Mutex::Mutex() Mutex::Mutex()
{ {
if ( pthread_mutex_init( &mMutex, NULL ) < 0 ) if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
} }
Mutex::~Mutex() Mutex::~Mutex()
{ {
if ( locked() ) if ( locked() )
Warning( "Destroying mutex when locked" ); Warning( "Destroying mutex when locked" );
if ( pthread_mutex_destroy( &mMutex ) < 0 ) if ( pthread_mutex_destroy( &mMutex ) < 0 )
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
} }
void Mutex::lock() void Mutex::lock()
{ {
if ( pthread_mutex_lock( &mMutex ) < 0 ) if ( pthread_mutex_lock( &mMutex ) < 0 )
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
} }
void Mutex::lock( int secs ) void Mutex::lock( int secs )
{ {
struct timespec timeout = getTimeout( secs ); struct timespec timeout = getTimeout( secs );
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
} }
void Mutex::lock( double secs ) void Mutex::lock( double secs )
{ {
struct timespec timeout = getTimeout( secs ); struct timespec timeout = getTimeout( secs );
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
} }
void Mutex::unlock() void Mutex::unlock()
{ {
if ( pthread_mutex_unlock( &mMutex ) < 0 ) if ( pthread_mutex_unlock( &mMutex ) < 0 )
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
} }
bool Mutex::locked() bool Mutex::locked()
{ {
int state = pthread_mutex_trylock( &mMutex ); int state = pthread_mutex_trylock( &mMutex );
if ( state != 0 && state != EBUSY ) if ( state != 0 && state != EBUSY )
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
if ( state != EBUSY ) if ( state != EBUSY )
unlock(); unlock();
return( state == EBUSY ); return( state == EBUSY );
} }
Condition::Condition( Mutex &mutex ) : mMutex( mutex ) Condition::Condition( Mutex &mutex ) : mMutex( mutex )
{ {
if ( pthread_cond_init( &mCondition, NULL ) < 0 ) if ( pthread_cond_init( &mCondition, NULL ) < 0 )
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
} }
Condition::~Condition() Condition::~Condition()
{ {
if ( pthread_cond_destroy( &mCondition ) < 0 ) if ( pthread_cond_destroy( &mCondition ) < 0 )
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
} }
void Condition::wait() void Condition::wait()
{ {
// Locking done outside of this function // Locking done outside of this function
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 ) if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
} }
bool Condition::wait( int secs ) bool Condition::wait( int secs )
{ {
// Locking done outside of this function // Locking done outside of this function
Debug( 8, "Waiting for %d seconds", secs ); Debug( 8, "Waiting for %d seconds", secs );
struct timespec timeout = getTimeout( secs ); struct timespec timeout = getTimeout( secs );
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
return( errno != ETIMEDOUT ); return( errno != ETIMEDOUT );
} }
bool Condition::wait( double secs ) bool Condition::wait( double secs )
{ {
// Locking done outside of this function // Locking done outside of this function
struct timespec timeout = getTimeout( secs ); struct timespec timeout = getTimeout( secs );
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
return( errno != ETIMEDOUT ); return( errno != ETIMEDOUT );
} }
void Condition::signal() void Condition::signal()
{ {
if ( pthread_cond_signal( &mCondition ) < 0 ) if ( pthread_cond_signal( &mCondition ) < 0 )
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
} }
void Condition::broadcast() void Condition::broadcast()
{ {
if ( pthread_cond_broadcast( &mCondition ) < 0 ) if ( pthread_cond_broadcast( &mCondition ) < 0 )
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
} }
template <class T> const T ThreadData<T>::getValue() const template <class T> const T ThreadData<T>::getValue() const
{ {
mMutex.lock(); mMutex.lock();
const T valueCopy = mValue; const T valueCopy = mValue;
mMutex.unlock(); mMutex.unlock();
return( valueCopy ); return( valueCopy );
} }
template <class T> T ThreadData<T>::setValue( const T value ) template <class T> T ThreadData<T>::setValue( const T value )
{ {
mMutex.lock(); mMutex.lock();
const T valueCopy = mValue = value; const T valueCopy = mValue = value;
mMutex.unlock(); mMutex.unlock();
return( valueCopy ); return( valueCopy );
} }
template <class T> const T ThreadData<T>::getUpdatedValue() const template <class T> const T ThreadData<T>::getUpdatedValue() const
{ {
Debug( 8, "Waiting for value update, %p", this ); Debug( 8, "Waiting for value update, %p", this );
mMutex.lock(); mMutex.lock();
mChanged = false; mChanged = false;
//do { //do {
mCondition.wait(); mCondition.wait();
//} while ( !mChanged ); //} while ( !mChanged );
const T valueCopy = mValue; const T valueCopy = mValue;
mMutex.unlock(); mMutex.unlock();
Debug( 9, "Got value update, %p", this ); Debug( 9, "Got value update, %p", this );
return( valueCopy ); return( valueCopy );
} }
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
{ {
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this ); Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
mMutex.lock(); mMutex.lock();
mChanged = false; mChanged = false;
//do { //do {
mCondition.wait( secs ); mCondition.wait( secs );
//} while ( !mChanged ); //} while ( !mChanged );
const T valueCopy = mValue; const T valueCopy = mValue;
mMutex.unlock(); mMutex.unlock();
Debug( 9, "Got value update, %p", this ); Debug( 9, "Got value update, %p", this );
return( valueCopy ); return( valueCopy );
} }
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
{ {
Debug( 8, "Waiting for value update, %d secs, %p", secs, this ); Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
mMutex.lock(); mMutex.lock();
mChanged = false; mChanged = false;
//do { //do {
mCondition.wait( secs ); mCondition.wait( secs );
//} while ( !mChanged ); //} while ( !mChanged );
const T valueCopy = mValue; const T valueCopy = mValue;
mMutex.unlock(); mMutex.unlock();
Debug( 9, "Got value update, %p", this ); Debug( 9, "Got value update, %p", this );
return( valueCopy ); return( valueCopy );
} }
template <class T> void ThreadData<T>::updateValueSignal( const T value ) template <class T> void ThreadData<T>::updateValueSignal( const T value )
{ {
Debug( 8, "Updating value with signal, %p", this ); Debug( 8, "Updating value with signal, %p", this );
mMutex.lock(); mMutex.lock();
mValue = value; mValue = value;
mChanged = true; mChanged = true;
mCondition.signal(); mCondition.signal();
mMutex.unlock(); mMutex.unlock();
Debug( 9, "Updated value, %p", this ); Debug( 9, "Updated value, %p", this );
} }
template <class T> void ThreadData<T>::updateValueBroadcast( const T value ) template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
{ {
Debug( 8, "Updating value with broadcast, %p", this ); Debug( 8, "Updating value with broadcast, %p", this );
mMutex.lock(); mMutex.lock();
mValue = value; mValue = value;
mChanged = true; mChanged = true;
mCondition.broadcast(); mCondition.broadcast();
mMutex.unlock(); mMutex.unlock();
Debug( 9, "Updated value, %p", this ); Debug( 9, "Updated value, %p", this );
} }
Thread::Thread() : Thread::Thread() :
mThreadCondition( mThreadMutex ), mThreadCondition( mThreadMutex ),
mPid( -1 ), mPid( -1 ),
mStarted( false ), mStarted( false ),
mRunning( false ) mRunning( false )
{ {
Debug( 1, "Creating thread" ); Debug( 1, "Creating thread" );
} }
Thread::~Thread() Thread::~Thread()
{ {
Debug( 1, "Destroying thread %d", mPid ); Debug( 1, "Destroying thread %d", mPid );
if ( mStarted ) if ( mStarted )
join(); join();
} }
void *Thread::mThreadFunc( void *arg ) void *Thread::mThreadFunc( void *arg )
{ {
Debug( 2, "Invoking thread" ); Debug( 2, "Invoking thread" );
Thread *thisPtr = (Thread *)arg; Thread *thisPtr = (Thread *)arg;
thisPtr->status = 0; thisPtr->status = 0;
try try
{ {
thisPtr->mThreadMutex.lock(); thisPtr->mThreadMutex.lock();
thisPtr->mPid = thisPtr->id(); thisPtr->mPid = thisPtr->id();
thisPtr->mThreadCondition.signal(); thisPtr->mThreadCondition.signal();
thisPtr->mThreadMutex.unlock(); thisPtr->mThreadMutex.unlock();
thisPtr->mRunning = true; thisPtr->mRunning = true;
thisPtr->status = thisPtr->run(); thisPtr->status = thisPtr->run();
thisPtr->mRunning = false; thisPtr->mRunning = false;
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) ); Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
return (void *)&(thisPtr->status); return (void *)&(thisPtr->status);
} }
catch ( const ThreadException &e ) catch ( const ThreadException &e )
{ {
Error( "%s", e.getMessage().c_str() ); Error( "%s", e.getMessage().c_str() );
thisPtr->mRunning = false; thisPtr->mRunning = false;
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 ); Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
return (void *)-1; return (void *)-1;
} }
} }
void Thread::start() void Thread::start()
{ {
Debug( 1, "Starting thread" ); Debug( 1, "Starting thread" );
if ( isThread() ) if ( isThread() )
throw ThreadException( "Can't self start thread" ); throw ThreadException( "Can't self start thread" );
mThreadMutex.lock(); mThreadMutex.lock();
if ( !mStarted ) if ( !mStarted )
{ {
pthread_attr_t threadAttrs; pthread_attr_t threadAttrs;
pthread_attr_init( &threadAttrs ); pthread_attr_init( &threadAttrs );
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM ); pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
mStarted = true; mStarted = true;
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 ) if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
pthread_attr_destroy( &threadAttrs ); pthread_attr_destroy( &threadAttrs );
} }
else else
{ {
Error( "Attempt to start already running thread %d", mPid ); Error( "Attempt to start already running thread %d", mPid );
} }
mThreadCondition.wait(); mThreadCondition.wait();
mThreadMutex.unlock(); mThreadMutex.unlock();
Debug( 1, "Started thread %d", mPid ); Debug( 1, "Started thread %d", mPid );
} }
void Thread::join() void Thread::join()
{ {
Debug( 1, "Joining thread %d", mPid ); Debug( 1, "Joining thread %d", mPid );
if ( isThread() ) if ( isThread() )
throw ThreadException( "Can't self join thread" ); throw ThreadException( "Can't self join thread" );
mThreadMutex.lock(); mThreadMutex.lock();
if ( mPid >= 0 ) if ( mPid >= 0 )
{
if ( mStarted )
{ {
if ( mStarted ) void *threadStatus = 0;
{ if ( pthread_join( mThread, &threadStatus ) < 0 )
void *threadStatus = 0; throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
if ( pthread_join( mThread, &threadStatus ) < 0 ) mStarted = false;
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) ); Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
mStarted = false;
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
}
else
{
Warning( "Attempt to join already finished thread %d", mPid );
}
} }
else else
{ {
Warning( "Attempt to join non-started thread %d", mPid ); Warning( "Attempt to join already finished thread %d", mPid );
} }
mThreadMutex.unlock(); }
Debug( 1, "Joined thread %d", mPid ); else
{
Warning( "Attempt to join non-started thread %d", mPid );
}
mThreadMutex.unlock();
Debug( 1, "Joined thread %d", mPid );
} }
void Thread::kill( int signal ) void Thread::kill( int signal )
{ {
pthread_kill( mThread, signal ); pthread_kill( mThread, signal );
} }
// Some explicit template instantiations // Some explicit template instantiations

View File

@ -36,27 +36,27 @@ class ThreadException : public Exception
{ {
private: private:
#ifndef SOLARIS #ifndef SOLARIS
pid_t pid() { pid_t pid() {
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
#ifdef __FreeBSD_kernel__ #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
# else # else
tid=syscall(SYS_gettid); tid=syscall(SYS_gettid);
#endif #endif
#endif #endif
return tid; return tid;
} }
#else #else
pthread_t pid() { return( pthread_self() ); } pthread_t pid() { return( pthread_self() ); }
#endif #endif
public: public:
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) { ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
} }
}; };
class Mutex class Mutex
@ -64,215 +64,215 @@ class Mutex
friend class Condition; friend class Condition;
private: private:
pthread_mutex_t mMutex; pthread_mutex_t mMutex;
public: public:
Mutex(); Mutex();
~Mutex(); ~Mutex();
private: private:
pthread_mutex_t *getMutex() pthread_mutex_t *getMutex()
{ {
return( &mMutex ); return( &mMutex );
} }
public: public:
void lock(); void lock();
void lock( int secs ); void lock( int secs );
void lock( double secs ); void lock( double secs );
void unlock(); void unlock();
bool locked(); bool locked();
}; };
class ScopedMutex class ScopedMutex
{ {
private: private:
Mutex &mMutex; Mutex &mMutex;
public: public:
ScopedMutex( Mutex &mutex ) : mMutex( mutex ) ScopedMutex( Mutex &mutex ) : mMutex( mutex )
{ {
mMutex.lock(); mMutex.lock();
} }
~ScopedMutex() ~ScopedMutex()
{ {
mMutex.unlock(); mMutex.unlock();
} }
private: private:
ScopedMutex( const ScopedMutex & ); ScopedMutex( const ScopedMutex & );
}; };
class Condition class Condition
{ {
private: private:
Mutex &mMutex; Mutex &mMutex;
pthread_cond_t mCondition; pthread_cond_t mCondition;
public: public:
Condition( Mutex &mutex ); Condition( Mutex &mutex );
~Condition(); ~Condition();
void wait(); void wait();
bool wait( int secs ); bool wait( int secs );
bool wait( double secs ); bool wait( double secs );
void signal(); void signal();
void broadcast(); void broadcast();
}; };
class Semaphore : public Condition class Semaphore : public Condition
{ {
private: private:
Mutex mMutex; Mutex mMutex;
public: public:
Semaphore() : Condition( mMutex ) Semaphore() : Condition( mMutex )
{ {
} }
void wait() void wait()
{ {
mMutex.lock(); mMutex.lock();
Condition::wait(); Condition::wait();
mMutex.unlock(); mMutex.unlock();
} }
bool wait( int secs ) bool wait( int secs )
{ {
mMutex.lock(); mMutex.lock();
bool result = Condition::wait( secs ); bool result = Condition::wait( secs );
mMutex.unlock(); mMutex.unlock();
return( result ); return( result );
} }
bool wait( double secs ) bool wait( double secs )
{ {
mMutex.lock(); mMutex.lock();
bool result = Condition::wait( secs ); bool result = Condition::wait( secs );
mMutex.unlock(); mMutex.unlock();
return( result ); return( result );
} }
void signal() void signal()
{ {
mMutex.lock(); mMutex.lock();
Condition::signal(); Condition::signal();
mMutex.unlock(); mMutex.unlock();
} }
void broadcast() void broadcast()
{ {
mMutex.lock(); mMutex.lock();
Condition::broadcast(); Condition::broadcast();
mMutex.unlock(); mMutex.unlock();
} }
}; };
template <class T> class ThreadData template <class T> class ThreadData
{ {
private: private:
T mValue; T mValue;
mutable bool mChanged; mutable bool mChanged;
mutable Mutex mMutex; mutable Mutex mMutex;
mutable Condition mCondition; mutable Condition mCondition;
public: public:
__attribute__((used)) ThreadData() : mCondition( mMutex ) __attribute__((used)) ThreadData() : mCondition( mMutex )
{ {
} }
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
{ {
} }
//~ThreadData() {} //~ThreadData() {}
__attribute__((used)) operator T() const __attribute__((used)) operator T() const
{ {
return( getValue() ); return( getValue() );
} }
__attribute__((used)) const T operator=( const T value ) __attribute__((used)) const T operator=( const T value )
{ {
return( setValue( value ) ); return( setValue( value ) );
} }
__attribute__((used)) const T getValueImmediate() const __attribute__((used)) const T getValueImmediate() const
{ {
return( mValue ); return( mValue );
} }
__attribute__((used)) T setValueImmediate( const T value ) __attribute__((used)) T setValueImmediate( const T value )
{ {
return( mValue = value ); return( mValue = value );
} }
__attribute__((used)) const T getValue() const; __attribute__((used)) const T getValue() const;
__attribute__((used)) T setValue( const T value ); __attribute__((used)) T setValue( const T value );
__attribute__((used)) const T getUpdatedValue() const; __attribute__((used)) const T getUpdatedValue() const;
__attribute__((used)) const T getUpdatedValue( double secs ) const; __attribute__((used)) const T getUpdatedValue( double secs ) const;
__attribute__((used)) const T getUpdatedValue( int secs ) const; __attribute__((used)) const T getUpdatedValue( int secs ) const;
__attribute__((used)) void updateValueSignal( const T value ); __attribute__((used)) void updateValueSignal( const T value );
__attribute__((used)) void updateValueBroadcast( const T value ); __attribute__((used)) void updateValueBroadcast( const T value );
}; };
class Thread class Thread
{ {
public: public:
typedef void *(*ThreadFunc)( void * ); typedef void *(*ThreadFunc)( void * );
protected: protected:
pthread_t mThread; pthread_t mThread;
Mutex mThreadMutex; Mutex mThreadMutex;
Condition mThreadCondition; Condition mThreadCondition;
#ifndef SOLARIS #ifndef SOLARIS
pid_t mPid; pid_t mPid;
#else #else
pthread_t mPid; pthread_t mPid;
#endif #endif
bool mStarted; bool mStarted;
bool mRunning; bool mRunning;
int status; // Used in various funcions to get around return a local variable int status; // Used in various funcions to get around return a local variable
protected: protected:
Thread(); Thread();
virtual ~Thread(); virtual ~Thread();
#ifndef SOLARIS #ifndef SOLARIS
pid_t id() const pid_t id() const
{ {
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
#ifdef __FreeBSD_kernel__ #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
#else #else
tid=syscall(SYS_gettid); tid=syscall(SYS_gettid);
#endif #endif
#endif #endif
return tid; return tid;
} }
#else #else
pthread_t id() const pthread_t id() const
{ {
return( pthread_self() ); return( pthread_self() );
} }
#endif #endif
void exit( int p_status = 0 ) void exit( int p_status = 0 )
{ {
//INFO( "Exiting" ); //INFO( "Exiting" );
pthread_exit( (void *)&p_status ); pthread_exit( (void *)&p_status );
} }
static void *mThreadFunc( void *arg ); static void *mThreadFunc( void *arg );
public: public:
virtual int run() = 0; virtual int run() = 0;
void start(); void start();
void join(); void join();
void kill( int signal ); void kill( int signal );
bool isThread() bool isThread()
{ {
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) ); return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
} }
bool isStarted() const { return( mStarted ); } bool isStarted() const { return( mStarted ); }
bool isRunning() const { return( mRunning ); } bool isRunning() const { return( mRunning ); }
}; };
#endif // ZM_THREAD_H #endif // ZM_THREAD_H

View File

@ -29,48 +29,48 @@
struct DeltaTimeval struct DeltaTimeval
{ {
bool positive; bool positive;
unsigned long delta; unsigned long delta;
unsigned long sec; unsigned long sec;
unsigned long fsec; unsigned long fsec;
unsigned long prec; unsigned long prec;
}; };
#define DT_GRAN_1000000 1000000 #define DT_GRAN_1000000 1000000
#define DT_PREC_6 DT_GRAN_1000000 #define DT_PREC_6 DT_GRAN_1000000
#define DT_GRAN_100000 100000 #define DT_GRAN_100000 100000
#define DT_PREC_5 DT_GRAN_100000 #define DT_PREC_5 DT_GRAN_100000
#define DT_GRAN_10000 10000 #define DT_GRAN_10000 10000
#define DT_PREC_4 DT_GRAN_10000 #define DT_PREC_4 DT_GRAN_10000
#define DT_GRAN_1000 1000 #define DT_GRAN_1000 1000
#define DT_PREC_3 DT_GRAN_1000 #define DT_PREC_3 DT_GRAN_1000
#define DT_GRAN_100 100 #define DT_GRAN_100 100
#define DT_PREC_2 DT_GRAN_100 #define DT_PREC_2 DT_GRAN_100
#define DT_GRAN_10 10 #define DT_GRAN_10 10
#define DT_PREC_1 DT_GRAN_10 #define DT_PREC_1 DT_GRAN_10
#define DT_MAXGRAN DT_GRAN_1000000 #define DT_MAXGRAN DT_GRAN_1000000
// This obviously wouldn't work for massive deltas but as it's mostly // This obviously wouldn't work for massive deltas but as it's mostly
// for frames it will only usually be a fraction of a second or so // for frames it will only usually be a fraction of a second or so
#define DELTA_TIMEVAL( result, time1, time2, precision ) \ #define DELTA_TIMEVAL( result, time1, time2, precision ) \
{ \ { \
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
result.positive = (delta>=0); \ result.positive = (delta>=0); \
result.delta = abs(delta); \ result.delta = abs(delta); \
result.sec = result.delta/(precision); \ result.sec = result.delta/(precision); \
result.fsec = result.delta%(precision); \ result.fsec = result.delta%(precision); \
result.prec = (precision); \ result.prec = (precision); \
} }
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \ #define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
{ \ { \
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
result.positive = (delta>=0); \ result.positive = (delta>=0); \
result.delta = abs(delta); \ result.delta = abs(delta); \
result.sec = result.delta/(precision); \ result.sec = result.delta/(precision); \
result.fsec = result.delta%(precision); \ result.fsec = result.delta%(precision); \
result.prec = (precision); \ result.prec = (precision); \
} }
#define USEC_PER_SEC 1000000 #define USEC_PER_SEC 1000000
@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t;
inline int tvDiffUsec( struct timeval first, struct timeval last ) inline int tvDiffUsec( struct timeval first, struct timeval last )
{ {
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC ); return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
} }
inline int tvDiffUsec( struct timeval first ) inline int tvDiffUsec( struct timeval first )
{ {
struct timeval now; struct timeval now;
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
return( tvDiffUsec( first, now ) ); return( tvDiffUsec( first, now ) );
} }
inline int tvDiffMsec( struct timeval first, struct timeval last ) inline int tvDiffMsec( struct timeval first, struct timeval last )
{ {
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC ); return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
} }
inline int tvDiffMsec( struct timeval first ) inline int tvDiffMsec( struct timeval first )
{ {
struct timeval now; struct timeval now;
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
return( tvDiffMsec( first, now ) ); return( tvDiffMsec( first, now ) );
} }
inline double tvDiffSec( struct timeval first, struct timeval last ) inline double tvDiffSec( struct timeval first, struct timeval last )
{ {
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) ); return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
} }
inline double tvDiffSec( struct timeval first ) inline double tvDiffSec( struct timeval first )
{ {
struct timeval now; struct timeval now;
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
return( tvDiffSec( first, now ) ); return( tvDiffSec( first, now ) );
} }
inline struct timeval tvZero() inline struct timeval tvZero()
{ {
struct timeval t = { 0, 0 }; struct timeval t = { 0, 0 };
return( t ); return( t );
} }
inline int tvIsZero( const struct timeval t ) inline int tvIsZero( const struct timeval t )
{ {
return( t.tv_sec == 0 && t.tv_usec == 0 ); return( t.tv_sec == 0 && t.tv_usec == 0 );
} }
inline int tvCmp( struct timeval t1, struct timeval t2 ) inline int tvCmp( struct timeval t1, struct timeval t2 )
{ {
if ( t1.tv_sec < t2.tv_sec ) if ( t1.tv_sec < t2.tv_sec )
return( -1 ); return( -1 );
if ( t1.tv_sec > t2.tv_sec ) if ( t1.tv_sec > t2.tv_sec )
return( 1 ); return( 1 );
if ( t1.tv_usec < t2.tv_usec ) if ( t1.tv_usec < t2.tv_usec )
return( -1 ); return( -1 );
if ( t1.tv_usec > t2.tv_usec ) if ( t1.tv_usec > t2.tv_usec )
return( 1 ); return( 1 );
return( 0 ); return( 0 );
} }
inline int tvEq( struct timeval t1, struct timeval t2 ) inline int tvEq( struct timeval t1, struct timeval t2 )
{ {
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec ); return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
} }
inline struct timeval tvNow( void ) inline struct timeval tvNow( void )
{ {
struct timeval t; struct timeval t;
gettimeofday( &t, NULL ); gettimeofday( &t, NULL );
return( t ); return( t );
} }
inline struct timeval tvCheck( struct timeval &t ) inline struct timeval tvCheck( struct timeval &t )
{ {
if ( t.tv_usec >= USEC_PER_SEC ) if ( t.tv_usec >= USEC_PER_SEC )
{ {
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec ); Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
t.tv_sec += t.tv_usec / USEC_PER_SEC; t.tv_sec += t.tv_usec / USEC_PER_SEC;
t.tv_usec %= USEC_PER_SEC; t.tv_usec %= USEC_PER_SEC;
} }
else if ( t.tv_usec < 0 ) else if ( t.tv_usec < 0 )
{ {
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec ); Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
t.tv_usec = 0; t.tv_usec = 0;
} }
return( t ); return( t );
} }
// Add t2 to t1 // Add t2 to t1
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 ) inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
{ {
tvCheck(t1); tvCheck(t1);
tvCheck(t2); tvCheck(t2);
t1.tv_sec += t2.tv_sec; t1.tv_sec += t2.tv_sec;
t1.tv_usec += t2.tv_usec; t1.tv_usec += t2.tv_usec;
if ( t1.tv_usec >= USEC_PER_SEC ) if ( t1.tv_usec >= USEC_PER_SEC )
{ {
t1.tv_sec++; t1.tv_sec++;
t1.tv_usec -= USEC_PER_SEC; t1.tv_usec -= USEC_PER_SEC;
} }
return( t1 ); return( t1 );
} }
// Subtract t2 from t1 // Subtract t2 from t1
inline struct timeval tvSub( struct timeval t1, struct timeval t2 ) inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
{ {
tvCheck(t1); tvCheck(t1);
tvCheck(t2); tvCheck(t2);
t1.tv_sec -= t2.tv_sec; t1.tv_sec -= t2.tv_sec;
t1.tv_usec -= t2.tv_usec; t1.tv_usec -= t2.tv_usec;
if ( t1.tv_usec < 0 ) if ( t1.tv_usec < 0 )
{ {
t1.tv_sec--; t1.tv_sec--;
t1.tv_usec += USEC_PER_SEC; t1.tv_usec += USEC_PER_SEC;
} }
return( t1 ) ; return( t1 ) ;
} }
inline struct timeval tvMake( time_t sec, suseconds_t usec ) inline struct timeval tvMake( time_t sec, suseconds_t usec )
{ {
struct timeval t; struct timeval t;
t.tv_sec = sec; t.tv_sec = sec;
t.tv_usec = usec; t.tv_usec = usec;
return( t ); return( t );
} }
#endif // ZM_TIME_H #endif // ZM_TIME_H

View File

@ -24,96 +24,96 @@
int Timer::TimerThread::mNextTimerId = 0; int Timer::TimerThread::mNextTimerId = 0;
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) : Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
mTimerId( 0 ), mTimerId( 0 ),
mTimer( timer ), mTimer( timer ),
mDuration( duration ), mDuration( duration ),
mRepeat( repeat ), mRepeat( repeat ),
mReset( false ), mReset( false ),
mExpiryFlag( true ) mExpiryFlag( true )
{ {
mAccessMutex.lock(); mAccessMutex.lock();
mTimerId = mNextTimerId++; mTimerId = mNextTimerId++;
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" ); Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
mAccessMutex.unlock(); mAccessMutex.unlock();
} }
Timer::TimerThread::~TimerThread() Timer::TimerThread::~TimerThread()
{ {
cancel(); cancel();
} }
void Timer::TimerThread::cancel() void Timer::TimerThread::cancel()
{ {
mAccessMutex.lock(); mAccessMutex.lock();
if ( mRunning ) if ( mRunning )
{ {
Debug( 4, "Cancelling timer %d", mTimerId ); Debug( 4, "Cancelling timer %d", mTimerId );
mRepeat = false; mRepeat = false;
mReset = false; mReset = false;
mExpiryFlag.updateValueSignal( false ); mExpiryFlag.updateValueSignal( false );
} }
mAccessMutex.unlock(); mAccessMutex.unlock();
} }
void Timer::TimerThread::reset() void Timer::TimerThread::reset()
{ {
mAccessMutex.lock(); mAccessMutex.lock();
if ( mRunning ) if ( mRunning )
{ {
Debug( 4, "Resetting timer" ); Debug( 4, "Resetting timer" );
mReset = true; mReset = true;
mExpiryFlag.updateValueSignal( false ); mExpiryFlag.updateValueSignal( false );
} }
else else
{ {
Error( "Attempting to reset expired timer %d", mTimerId ); Error( "Attempting to reset expired timer %d", mTimerId );
} }
mAccessMutex.unlock(); mAccessMutex.unlock();
} }
int Timer::TimerThread::run() int Timer::TimerThread::run()
{ {
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration ); Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
bool timerExpired = false; bool timerExpired = false;
do do
{
mAccessMutex.lock();
mReset = false;
mExpiryFlag.setValue( true );
mAccessMutex.unlock();
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
mAccessMutex.lock();
if ( timerExpired )
{ {
mAccessMutex.lock(); Debug( 4, "Timer %d expired", mTimerId );
mReset = false; mTimer.expire();
mExpiryFlag.setValue( true ); }
mAccessMutex.unlock(); else
timerExpired = mExpiryFlag.getUpdatedValue( mDuration ); {
mAccessMutex.lock(); Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
if ( timerExpired ) }
{ mAccessMutex.unlock();
Debug( 4, "Timer %d expired", mTimerId ); } while ( mRepeat || (mReset && !timerExpired) );
mTimer.expire(); return( timerExpired );
}
else
{
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
}
mAccessMutex.unlock();
} while ( mRepeat || (mReset && !timerExpired) );
return( timerExpired );
} }
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat ) Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
{ {
mTimerThread.start(); mTimerThread.start();
} }
Timer::~Timer() Timer::~Timer()
{ {
//cancel(); //cancel();
} }
void Timer::Timer::cancel() void Timer::Timer::cancel()
{ {
mTimerThread.cancel(); mTimerThread.cancel();
} }
void Timer::Timer::reset() void Timer::Timer::reset()
{ {
mTimerThread.reset(); mTimerThread.reset();
} }

View File

@ -30,81 +30,81 @@
class Timer class Timer
{ {
private: private:
class TimerException : public Exception class TimerException : public Exception
{ {
private: private:
#ifndef SOLARIS #ifndef SOLARIS
pid_t pid() { pid_t pid() {
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
thr_self(&lwpid); thr_self(&lwpid);
tid = lwpid; tid = lwpid;
#else #else
#ifdef __FreeBSD_kernel__ #ifdef __FreeBSD_kernel__
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
#else #else
tid=syscall(SYS_gettid); tid=syscall(SYS_gettid);
#endif #endif
#endif #endif
return tid; return tid;
} }
#else #else
pthread_t pid() { return( pthread_self() ); } pthread_t pid() { return( pthread_self() ); }
#endif #endif
public: public:
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
{
}
};
class TimerThread : public Thread
{ {
private: }
typedef ThreadData<bool> ExpiryFlag; };
private: class TimerThread : public Thread
static int mNextTimerId; {
private:
typedef ThreadData<bool> ExpiryFlag;
private: private:
int mTimerId; static int mNextTimerId;
Timer &mTimer;
int mDuration;
int mRepeat;
int mReset;
ExpiryFlag mExpiryFlag;
Mutex mAccessMutex;
private: private:
void quit() int mTimerId;
{ Timer &mTimer;
cancel(); int mDuration;
} int mRepeat;
int mReset;
ExpiryFlag mExpiryFlag;
Mutex mAccessMutex;
public: private:
TimerThread( Timer &timer, int timeout, bool repeat ); void quit()
~TimerThread(); {
cancel();
}
void cancel(); public:
void reset(); TimerThread( Timer &timer, int timeout, bool repeat );
int run(); ~TimerThread();
};
protected:
TimerThread mTimerThread;
protected:
Timer( int timeout, bool repeat=false );
public:
virtual ~Timer();
protected:
virtual void expire()=0;
public:
void cancel(); void cancel();
void reset(); void reset();
int run();
};
protected:
TimerThread mTimerThread;
protected:
Timer( int timeout, bool repeat=false );
public:
virtual ~Timer();
protected:
virtual void expire()=0;
public:
void cancel();
void reset();
}; };
#endif // ZM_TIMER_H #endif // ZM_TIMER_H

View File

@ -29,119 +29,119 @@
User::User() User::User()
{ {
username[0] = password[0] = 0; username[0] = password[0] = 0;
enabled = false; enabled = false;
stream = events = control = monitors = system = PERM_NONE; stream = events = control = monitors = system = PERM_NONE;
monitor_ids = 0; monitor_ids = 0;
} }
User::User( MYSQL_ROW &dbrow ) User::User( MYSQL_ROW &dbrow )
{ {
int index = 0; int index = 0;
strncpy( username, dbrow[index++], sizeof(username) ); strncpy( username, dbrow[index++], sizeof(username) );
strncpy( password, dbrow[index++], sizeof(password) ); strncpy( password, dbrow[index++], sizeof(password) );
enabled = (bool)atoi( dbrow[index++] ); enabled = (bool)atoi( dbrow[index++] );
stream = (Permission)atoi( dbrow[index++] ); stream = (Permission)atoi( dbrow[index++] );
events = (Permission)atoi( dbrow[index++] ); events = (Permission)atoi( dbrow[index++] );
control = (Permission)atoi( dbrow[index++] ); control = (Permission)atoi( dbrow[index++] );
monitors = (Permission)atoi( dbrow[index++] ); monitors = (Permission)atoi( dbrow[index++] );
system = (Permission)atoi( dbrow[index++] ); system = (Permission)atoi( dbrow[index++] );
monitor_ids = 0; monitor_ids = 0;
char *monitor_ids_str = dbrow[index++]; char *monitor_ids_str = dbrow[index++];
if ( monitor_ids_str && *monitor_ids_str ) if ( monitor_ids_str && *monitor_ids_str )
{ {
monitor_ids = new int[strlen(monitor_ids_str)]; monitor_ids = new int[strlen(monitor_ids_str)];
int n_monitor_ids = 0; int n_monitor_ids = 0;
const char *ptr = monitor_ids_str; const char *ptr = monitor_ids_str;
do do
{ {
int id = 0; int id = 0;
while( isdigit( *ptr ) ) while( isdigit( *ptr ) )
{ {
id *= 10; id *= 10;
id += *ptr-'0'; id += *ptr-'0';
ptr++; ptr++;
} }
if ( id ) if ( id )
{ {
monitor_ids[n_monitor_ids++] = id; monitor_ids[n_monitor_ids++] = id;
if ( !*ptr ) if ( !*ptr )
break; break;
} }
while ( !isdigit( *ptr ) ) while ( !isdigit( *ptr ) )
ptr++; ptr++;
} while( *ptr ); } while( *ptr );
monitor_ids[n_monitor_ids] = 0; monitor_ids[n_monitor_ids] = 0;
} }
} }
User::~User() User::~User()
{ {
delete monitor_ids; delete monitor_ids;
} }
bool User::canAccess( int monitor_id ) bool User::canAccess( int monitor_id )
{ {
if ( !monitor_ids ) if ( !monitor_ids )
{ {
return( true ); return( true );
} }
for ( int i = 0; monitor_ids[i]; i++ ) for ( int i = 0; monitor_ids[i]; i++ )
{ {
if ( monitor_ids[i] == monitor_id ) if ( monitor_ids[i] == monitor_id )
{ {
return( true ); return( true );
} }
} }
return( false ); return( false );
} }
// Function to load a user from username and password // Function to load a user from username and password
// Please note that in auth relay mode = none, password is NULL // Please note that in auth relay mode = none, password is NULL
User *zmLoadUser( const char *username, const char *password ) User *zmLoadUser( const char *username, const char *password )
{ {
char sql[ZM_SQL_SML_BUFSIZ] = ""; char sql[ZM_SQL_SML_BUFSIZ] = "";
char safer_username[65]; // current db username size is 32 char safer_username[65]; // current db username size is 32
char safer_password[129]; // current db password size is 64 char safer_password[129]; // current db password size is 64
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) ); mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
if ( password ) { if ( password ) {
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) ); mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password ); snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
} else { } else {
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username ); snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
} }
if ( mysql_query( &dbconn, sql ) ) if ( mysql_query( &dbconn, sql ) )
{ {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result ) if ( !result )
{ {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
int n_users = mysql_num_rows( result ); int n_users = mysql_num_rows( result );
if ( n_users != 1 ) if ( n_users != 1 )
{ {
Warning( "Unable to authenticate user %s", username ); Warning( "Unable to authenticate user %s", username );
return( 0 ); return( 0 );
} }
MYSQL_ROW dbrow = mysql_fetch_row( result ); MYSQL_ROW dbrow = mysql_fetch_row( result );
User *user = new User( dbrow ); User *user = new User( dbrow );
Info( "Authenticated user '%s'", user->getUsername() ); Info( "Authenticated user '%s'", user->getUsername() );
mysql_free_result( result ); mysql_free_result( result );
return( user ); return( user );
} }
// Function to validate an authentication string // Function to validate an authentication string
@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
{ {
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
#ifdef HAVE_GCRYPT_H #ifdef HAVE_GCRYPT_H
// Special initialisation for libgcrypt // Special initialisation for libgcrypt
if ( !gcry_check_version( GCRYPT_VERSION ) ) if ( !gcry_check_version( GCRYPT_VERSION ) )
{ {
Fatal( "Unable to initialise libgcrypt" ); Fatal( "Unable to initialise libgcrypt" );
} }
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
#endif // HAVE_GCRYPT_H #endif // HAVE_GCRYPT_H
const char *remote_addr = ""; const char *remote_addr = "";
if ( use_remote_addr ) if ( use_remote_addr )
{ {
remote_addr = getenv( "REMOTE_ADDR" ); remote_addr = getenv( "REMOTE_ADDR" );
if ( !remote_addr ) if ( !remote_addr )
{ {
Warning( "Can't determine remote address, using null" ); Warning( "Can't determine remote address, using null" );
remote_addr = ""; remote_addr = "";
} }
} }
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
char sql[ZM_SQL_SML_BUFSIZ] = ""; char sql[ZM_SQL_SML_BUFSIZ] = "";
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" ); snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
if ( mysql_query( &dbconn, sql ) ) if ( mysql_query( &dbconn, sql ) )
{ {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result ) if ( !result )
{ {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
int n_users = mysql_num_rows( result ); int n_users = mysql_num_rows( result );
if ( n_users < 1 ) if ( n_users < 1 )
{ {
Warning( "Unable to authenticate user" ); Warning( "Unable to authenticate user" );
return( 0 ); return( 0 );
} }
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
{ {
const char *user = dbrow[0]; const char *user = dbrow[0];
const char *pass = dbrow[1]; const char *pass = dbrow[1];
char auth_key[512] = ""; char auth_key[512] = "";
char auth_md5[32+1] = ""; char auth_md5[32+1] = "";
size_t md5len = 16; size_t md5len = 16;
unsigned char md5sum[md5len]; unsigned char md5sum[md5len];
time_t now = time( 0 ); time_t now = time( 0 );
int max_tries = 2; int max_tries = 2;
for ( int i = 0; i < max_tries; i++, now -= (60*60) ) for ( int i = 0; i < max_tries; i++, now -= (60*60) )
{ {
struct tm *now_tm = localtime( &now ); struct tm *now_tm = localtime( &now );
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
config.auth_hash_secret, config.auth_hash_secret,
user, user,
pass, pass,
remote_addr, remote_addr,
now_tm->tm_hour, now_tm->tm_hour,
now_tm->tm_mday, now_tm->tm_mday,
now_tm->tm_mon, now_tm->tm_mon,
now_tm->tm_year now_tm->tm_year
); );
#if HAVE_DECL_MD5 #if HAVE_DECL_MD5
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
#elif HAVE_DECL_GNUTLS_FINGERPRINT #elif HAVE_DECL_GNUTLS_FINGERPRINT
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
#endif #endif
auth_md5[0] = '\0'; auth_md5[0] = '\0';
for ( unsigned int j = 0; j < md5len; j++ ) for ( unsigned int j = 0; j < md5len; j++ )
{ {
sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
} }
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 ); Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
if ( !strcmp( auth, auth_md5 ) ) if ( !strcmp( auth, auth_md5 ) )
{ {
// We have a match // We have a match
User *user = new User( dbrow ); User *user = new User( dbrow );
Debug(1, "Authenticated user '%s'", user->getUsername() ); Debug(1, "Authenticated user '%s'", user->getUsername() );
return( user ); return( user );
} }
} }
} }
#else // HAVE_DECL_MD5 #else // HAVE_DECL_MD5
Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
#endif // HAVE_DECL_MD5 #endif // HAVE_DECL_MD5
return( 0 ); return( 0 );
} }

View File

@ -39,33 +39,33 @@
class User class User
{ {
public: public:
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission; typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
protected: protected:
char username[32+1]; char username[32+1];
char password[64+1]; char password[64+1];
bool enabled; bool enabled;
Permission stream; Permission stream;
Permission events; Permission events;
Permission control; Permission control;
Permission monitors; Permission monitors;
Permission system; Permission system;
int *monitor_ids; int *monitor_ids;
public: public:
User(); User();
User( MYSQL_ROW &dbrow ); User( MYSQL_ROW &dbrow );
~User(); ~User();
const char *getUsername() const { return( username ); } const char *getUsername() const { return( username ); }
const char *getPassword() const { return( password ); } const char *getPassword() const { return( password ); }
bool isEnabled() const { return( enabled ); } bool isEnabled() const { return( enabled ); }
Permission getStream() const { return( stream ); } Permission getStream() const { return( stream ); }
Permission getEvents() const { return( events ); } Permission getEvents() const { return( events ); }
Permission getControl() const { return( control ); } Permission getControl() const { return( control ); }
Permission getMonitors() const { return( monitors ); } Permission getMonitors() const { return( monitors ); }
Permission getSystem() const { return( system ); } Permission getSystem() const { return( system ); }
bool canAccess( int monitor_id ); bool canAccess( int monitor_id );
}; };
User *zmLoadUser( const char *username, const char *password=0 ); User *zmLoadUser( const char *username, const char *password=0 );

View File

@ -28,256 +28,256 @@
unsigned int sseversion = 0; unsigned int sseversion = 0;
std::string trimSet(std::string str, std::string trimset) { std::string trimSet(std::string str, std::string trimset) {
// Trim Both leading and trailing sets // Trim Both leading and trailing sets
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
// if all spaces or empty return an empty string // if all spaces or empty return an empty string
if(( std::string::npos == startpos ) || ( std::string::npos == endpos)) if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
{ {
return std::string(""); return std::string("");
} }
else else
return str.substr( startpos, endpos-startpos+1 ); return str.substr( startpos, endpos-startpos+1 );
} }
std::string trimSpaces(std::string str) std::string trimSpaces(std::string str)
{ {
return trimSet(str, " \t"); return trimSet(str, " \t");
} }
std::string replaceAll(std::string str, std::string from, std::string to) { std::string replaceAll(std::string str, std::string from, std::string to) {
if(from.empty()) if(from.empty())
return str;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
return str; return str;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
return str;
} }
const std::string stringtf( const char *format, ... ) const std::string stringtf( const char *format, ... )
{ {
va_list ap; va_list ap;
char tempBuffer[8192]; char tempBuffer[8192];
std::string tempString; std::string tempString;
va_start(ap, format ); va_start(ap, format );
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap ); vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
va_end(ap); va_end(ap);
tempString = tempBuffer; tempString = tempBuffer;
return( tempString ); return( tempString );
} }
const std::string stringtf( const std::string &format, ... ) const std::string stringtf( const std::string &format, ... )
{ {
va_list ap; va_list ap;
char tempBuffer[8192]; char tempBuffer[8192];
std::string tempString; std::string tempString;
va_start(ap, format ); va_start(ap, format );
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap ); vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
va_end(ap); va_end(ap);
tempString = tempBuffer; tempString = tempBuffer;
return( tempString ); return( tempString );
} }
bool startsWith( const std::string &haystack, const std::string &needle ) bool startsWith( const std::string &haystack, const std::string &needle )
{ {
return( haystack.substr( 0, needle.length() ) == needle ); return( haystack.substr( 0, needle.length() ) == needle );
} }
StringVector split( const std::string &string, const std::string chars, int limit ) StringVector split( const std::string &string, const std::string chars, int limit )
{ {
StringVector stringVector; StringVector stringVector;
std::string tempString = string; std::string tempString = string;
std::string::size_type startIndex = 0; std::string::size_type startIndex = 0;
std::string::size_type endIndex = 0; std::string::size_type endIndex = 0;
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit ); //Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
do do
{
// Find delimiters
endIndex = string.find_first_of( chars, startIndex );
//Info( "Got endIndex at %d", endIndex );
if ( endIndex > 0 )
{ {
// Find delimiters //Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
endIndex = string.find_first_of( chars, startIndex ); stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
//Info( "Got endIndex at %d", endIndex ); }
if ( endIndex > 0 ) if ( endIndex == std::string::npos )
{ break;
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() ); // Find non-delimiters
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) ); startIndex = tempString.find_first_not_of( chars, endIndex );
} if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
if ( endIndex == std::string::npos ) {
break; stringVector.push_back( string.substr( startIndex ) );
// Find non-delimiters break;
startIndex = tempString.find_first_not_of( chars, endIndex ); }
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) ) //Info( "Got new startIndex at %d", startIndex );
{ } while ( startIndex != std::string::npos );
stringVector.push_back( string.substr( startIndex ) ); //Info( "Finished with %d strings", stringVector.size() );
break;
}
//Info( "Got new startIndex at %d", startIndex );
} while ( startIndex != std::string::npos );
//Info( "Finished with %d strings", stringVector.size() );
return( stringVector ); return( stringVector );
} }
const std::string join(const StringVector v, const char * delim ) { const std::string join(const StringVector v, const char * delim ) {
std::stringstream ss; std::stringstream ss;
for(size_t i = 0; i < v.size(); ++i) { for(size_t i = 0; i < v.size(); ++i) {
if(i != 0) if(i != 0)
ss << ","; ss << ",";
ss << v[i]; ss << v[i];
} }
return ss.str(); return ss.str();
} }
const std::string base64Encode( const std::string &inString ) const std::string base64Encode( const std::string &inString )
{ {
static char base64_table[64] = { '\0' }; static char base64_table[64] = { '\0' };
if ( !base64_table[0] ) if ( !base64_table[0] )
{ {
int i = 0; int i = 0;
for ( char c = 'A'; c <= 'Z'; c++ ) for ( char c = 'A'; c <= 'Z'; c++ )
base64_table[i++] = c; base64_table[i++] = c;
for ( char c = 'a'; c <= 'z'; c++ ) for ( char c = 'a'; c <= 'z'; c++ )
base64_table[i++] = c; base64_table[i++] = c;
for ( char c = '0'; c <= '9'; c++ ) for ( char c = '0'; c <= '9'; c++ )
base64_table[i++] = c; base64_table[i++] = c;
base64_table[i++] = '+'; base64_table[i++] = '+';
base64_table[i++] = '/'; base64_table[i++] = '/';
} }
std::string outString; std::string outString;
outString.reserve( 2 * inString.size() ); outString.reserve( 2 * inString.size() );
const char *inPtr = inString.c_str(); const char *inPtr = inString.c_str();
while( *inPtr ) while( *inPtr )
{ {
unsigned char selection = *inPtr >> 2; unsigned char selection = *inPtr >> 2;
unsigned char remainder = (*inPtr++ & 0x03) << 4; unsigned char remainder = (*inPtr++ & 0x03) << 4;
outString += base64_table[selection]; outString += base64_table[selection];
if ( *inPtr ) if ( *inPtr )
{ {
selection = remainder | (*inPtr >> 4); selection = remainder | (*inPtr >> 4);
remainder = (*inPtr++ & 0x0f) << 2; remainder = (*inPtr++ & 0x0f) << 2;
outString += base64_table[selection]; outString += base64_table[selection];
if ( *inPtr ) if ( *inPtr )
{ {
selection = remainder | (*inPtr >> 6); selection = remainder | (*inPtr >> 6);
outString += base64_table[selection]; outString += base64_table[selection];
selection = (*inPtr++ & 0x3f); selection = (*inPtr++ & 0x3f);
outString += base64_table[selection]; outString += base64_table[selection];
} }
else else
{ {
outString += base64_table[remainder]; outString += base64_table[remainder];
outString += '='; outString += '=';
} }
} }
else else
{ {
outString += base64_table[remainder]; outString += base64_table[remainder];
outString += '='; outString += '=';
outString += '='; outString += '=';
} }
} }
return( outString ); return( outString );
} }
int split(const char* string, const char delim, std::vector<std::string>& items) { int split(const char* string, const char delim, std::vector<std::string>& items) {
if(string == NULL) if(string == NULL)
return -1; return -1;
if(string[0] == 0) if(string[0] == 0)
return -2; return -2;
std::string str(string); std::string str(string);
size_t pos; size_t pos;
while(true) { while(true) {
pos = str.find(delim); pos = str.find(delim);
items.push_back(str.substr(0, pos)); items.push_back(str.substr(0, pos));
str.erase(0, pos+1); str.erase(0, pos+1);
if(pos == std::string::npos) if(pos == std::string::npos)
break; break;
} }
return items.size(); return items.size();
} }
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) { int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
if(string == NULL) if(string == NULL)
return -1; return -1;
if(string[0] == 0) if(string[0] == 0)
return -2; return -2;
std::string str(string); std::string str(string);
size_t pos = str.find(delim); size_t pos = str.find(delim);
if(pos == std::string::npos || pos == 0 || pos >= str.length()) if(pos == std::string::npos || pos == 0 || pos >= str.length())
return -3; return -3;
name = str.substr(0, pos); name = str.substr(0, pos);
value = str.substr(pos+1, std::string::npos); value = str.substr(pos+1, std::string::npos);
return 0; return 0;
} }
/* Sets sse_version */ /* Sets sse_version */
void ssedetect() { void ssedetect() {
#if (defined(__i386__) || defined(__x86_64__)) #if (defined(__i386__) || defined(__x86_64__))
/* x86 or x86-64 processor */ /* x86 or x86-64 processor */
uint32_t r_edx, r_ecx; uint32_t r_edx, r_ecx;
__asm__ __volatile__( __asm__ __volatile__(
#if defined(__i386__) #if defined(__i386__)
"pushl %%ebx;\n\t" "pushl %%ebx;\n\t"
#endif #endif
"mov $0x1,%%eax\n\t" "mov $0x1,%%eax\n\t"
"cpuid\n\t" "cpuid\n\t"
#if defined(__i386__) #if defined(__i386__)
"popl %%ebx;\n\t" "popl %%ebx;\n\t"
#endif #endif
: "=d" (r_edx), "=c" (r_ecx) : "=d" (r_edx), "=c" (r_ecx)
: :
: "%eax" : "%eax"
#if !defined(__i386__) #if !defined(__i386__)
, "%ebx" , "%ebx"
#endif #endif
); );
if (r_ecx & 0x00000200) { if (r_ecx & 0x00000200) {
sseversion = 35; /* SSSE3 */ sseversion = 35; /* SSSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSSE3"); Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
} else if (r_ecx & 0x00000001) { } else if (r_ecx & 0x00000001) {
sseversion = 30; /* SSE3 */ sseversion = 30; /* SSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSE3"); Debug(1,"Detected a x86\\x86-64 processor with SSE3");
} else if (r_edx & 0x04000000) { } else if (r_edx & 0x04000000) {
sseversion = 20; /* SSE2 */ sseversion = 20; /* SSE2 */
Debug(1,"Detected a x86\\x86-64 processor with SSE2"); Debug(1,"Detected a x86\\x86-64 processor with SSE2");
} else if (r_edx & 0x02000000) { } else if (r_edx & 0x02000000) {
sseversion = 10; /* SSE */ sseversion = 10; /* SSE */
Debug(1,"Detected a x86\\x86-64 processor with SSE"); Debug(1,"Detected a x86\\x86-64 processor with SSE");
} else { } else {
sseversion = 0; sseversion = 0;
Debug(1,"Detected a x86\\x86-64 processor"); Debug(1,"Detected a x86\\x86-64 processor");
} }
#else #else
/* Non x86 or x86-64 processor, SSE2 is not available */ /* Non x86 or x86-64 processor, SSE2 is not available */
Debug(1,"Detected a non x86\\x86-64 processor"); Debug(1,"Detected a non x86\\x86-64 processor");
sseversion = 0; sseversion = 0;
#endif #endif
} }
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
#endif #endif
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) { void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
if(bytes > 128) { if(bytes > 128) {
unsigned int remainder = bytes % 128; unsigned int remainder = bytes % 128;
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
__asm__ __volatile__( __asm__ __volatile__(
"sse2_copy_iter:\n\t" "sse2_copy_iter:\n\t"
"movdqa (%0),%%xmm0\n\t" "movdqa (%0),%%xmm0\n\t"
"movdqa 0x10(%0),%%xmm1\n\t" "movdqa 0x10(%0),%%xmm1\n\t"
"movdqa 0x20(%0),%%xmm2\n\t" "movdqa 0x20(%0),%%xmm2\n\t"
"movdqa 0x30(%0),%%xmm3\n\t" "movdqa 0x30(%0),%%xmm3\n\t"
"movdqa 0x40(%0),%%xmm4\n\t" "movdqa 0x40(%0),%%xmm4\n\t"
"movdqa 0x50(%0),%%xmm5\n\t" "movdqa 0x50(%0),%%xmm5\n\t"
"movdqa 0x60(%0),%%xmm6\n\t" "movdqa 0x60(%0),%%xmm6\n\t"
"movdqa 0x70(%0),%%xmm7\n\t" "movdqa 0x70(%0),%%xmm7\n\t"
"movntdq %%xmm0,(%1)\n\t" "movntdq %%xmm0,(%1)\n\t"
"movntdq %%xmm1,0x10(%1)\n\t" "movntdq %%xmm1,0x10(%1)\n\t"
"movntdq %%xmm2,0x20(%1)\n\t" "movntdq %%xmm2,0x20(%1)\n\t"
"movntdq %%xmm3,0x30(%1)\n\t" "movntdq %%xmm3,0x30(%1)\n\t"
"movntdq %%xmm4,0x40(%1)\n\t" "movntdq %%xmm4,0x40(%1)\n\t"
"movntdq %%xmm5,0x50(%1)\n\t" "movntdq %%xmm5,0x50(%1)\n\t"
"movntdq %%xmm6,0x60(%1)\n\t" "movntdq %%xmm6,0x60(%1)\n\t"
"movntdq %%xmm7,0x70(%1)\n\t" "movntdq %%xmm7,0x70(%1)\n\t"
"add $0x80, %0\n\t" "add $0x80, %0\n\t"
"add $0x80, %1\n\t" "add $0x80, %1\n\t"
"cmp %2, %0\n\t" "cmp %2, %0\n\t"
"jb sse2_copy_iter\n\t" "jb sse2_copy_iter\n\t"
"test %3, %3\n\t" "test %3, %3\n\t"
"jz sse2_copy_finish\n\t" "jz sse2_copy_finish\n\t"
"cld\n\t" "cld\n\t"
"rep movsb\n\t" "rep movsb\n\t"
"sse2_copy_finish:\n\t" "sse2_copy_finish:\n\t"
: :
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
); );
} else { } else {
/* Standard memcpy */ /* Standard memcpy */
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory"); __asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
} }
#else #else
/* Non x86\x86-64 platform, use memcpy */ /* Non x86\x86-64 platform, use memcpy */
memcpy(dest,src,bytes); memcpy(dest,src,bytes);
#endif #endif
return dest; return dest;
} }
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) { void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
if (((end->tv_nsec)-(start->tv_nsec))<0) { if (((end->tv_nsec)-(start->tv_nsec))<0) {
diff->tv_sec = end->tv_sec-start->tv_sec-1; diff->tv_sec = end->tv_sec-start->tv_sec-1;
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec; diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
} else { } else {
diff->tv_sec = end->tv_sec-start->tv_sec; diff->tv_sec = end->tv_sec-start->tv_sec;
diff->tv_nsec = end->tv_nsec-start->tv_nsec; diff->tv_nsec = end->tv_nsec-start->tv_nsec;
} }
} }

View File

@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
inline int max( int a, int b ) inline int max( int a, int b )
{ {
return( a>=b?a:b ); return( a>=b?a:b );
} }
inline int min( int a, int b ) inline int min( int a, int b )
{ {
return( a<=b?a:b ); return( a<=b?a:b );
} }
void ssedetect(); void ssedetect();

File diff suppressed because it is too large Load Diff

View File

@ -35,141 +35,141 @@ class Monitor;
class Zone class Zone
{ {
protected: protected:
struct Range struct Range
{ {
int lo_x; int lo_x;
int hi_x; int hi_x;
int off_x; int off_x;
}; };
public: public:
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType; typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod; typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
protected: protected:
// Inputs // Inputs
Monitor *monitor; Monitor *monitor;
int id; int id;
char *label; char *label;
ZoneType type; ZoneType type;
Polygon polygon; Polygon polygon;
Rgb alarm_rgb; Rgb alarm_rgb;
CheckMethod check_method; CheckMethod check_method;
int min_pixel_threshold; int min_pixel_threshold;
int max_pixel_threshold; int max_pixel_threshold;
int min_alarm_pixels; int min_alarm_pixels;
int max_alarm_pixels; int max_alarm_pixels;
Coord filter_box; Coord filter_box;
int min_filter_pixels; int min_filter_pixels;
int max_filter_pixels; int max_filter_pixels;
int min_blob_pixels; int min_blob_pixels;
int max_blob_pixels; int max_blob_pixels;
int min_blobs; int min_blobs;
int max_blobs; int max_blobs;
int overload_frames; int overload_frames;
int extend_alarm_frames; int extend_alarm_frames;
// Outputs/Statistics // Outputs/Statistics
bool alarmed; bool alarmed;
int pixel_diff; int pixel_diff;
unsigned int alarm_pixels; unsigned int alarm_pixels;
int alarm_filter_pixels; int alarm_filter_pixels;
int alarm_blob_pixels; int alarm_blob_pixels;
int alarm_blobs; int alarm_blobs;
int min_blob_size; int min_blob_size;
int max_blob_size; int max_blob_size;
Box alarm_box; Box alarm_box;
Coord alarm_centre; Coord alarm_centre;
unsigned int score; unsigned int score;
Image *pg_image; Image *pg_image;
Range *ranges; Range *ranges;
Image *image; Image *image;
int overload_count; int overload_count;
int extend_alarm_count; int extend_alarm_count;
protected: protected:
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ); void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
public: public:
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 ) Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
{ {
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
} }
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0) Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
{ {
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
} }
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon ) Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
{ {
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
} }
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon ) Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
{ {
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
} }
public: public:
~Zone(); ~Zone();
inline int Id() const { return( id ); } inline int Id() const { return( id ); }
inline const char *Label() const { return( label ); } inline const char *Label() const { return( label ); }
inline ZoneType Type() const { return( type ); } inline ZoneType Type() const { return( type ); }
inline bool IsActive() const { return( type == ACTIVE ); } inline bool IsActive() const { return( type == ACTIVE ); }
inline bool IsInclusive() const { return( type == INCLUSIVE ); } inline bool IsInclusive() const { return( type == INCLUSIVE ); }
inline bool IsExclusive() const { return( type == EXCLUSIVE ); } inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); } inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
inline bool IsInactive() const { return( type == INACTIVE ); } inline bool IsInactive() const { return( type == INACTIVE ); }
inline bool IsPrivacy() const { return( type == PRIVACY ); } inline bool IsPrivacy() const { return( type == PRIVACY ); }
inline const Image *AlarmImage() const { return( image ); } inline const Image *AlarmImage() const { return( image ); }
inline const Polygon &GetPolygon() const { return( polygon ); } inline const Polygon &GetPolygon() const { return( polygon ); }
inline bool Alarmed() const { return( alarmed ); } inline bool Alarmed() const { return( alarmed ); }
inline void SetAlarm() { alarmed = true; } inline void SetAlarm() { alarmed = true; }
inline void ClearAlarm() { alarmed = false; } inline void ClearAlarm() { alarmed = false; }
inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline Coord GetAlarmCentre() const { return( alarm_centre ); }
inline unsigned int Score() const { return( score ); } inline unsigned int Score() const { return( score ); }
inline void ResetStats() inline void ResetStats()
{ {
alarmed = false; alarmed = false;
pixel_diff = 0; pixel_diff = 0;
alarm_pixels = 0; alarm_pixels = 0;
alarm_filter_pixels = 0; alarm_filter_pixels = 0;
alarm_blob_pixels = 0; alarm_blob_pixels = 0;
alarm_blobs = 0; alarm_blobs = 0;
min_blob_size = 0; min_blob_size = 0;
max_blob_size = 0; max_blob_size = 0;
score = 0; score = 0;
} }
void RecordStats( const Event *event ); void RecordStats( const Event *event );
bool CheckAlarms( const Image *delta_image ); bool CheckAlarms( const Image *delta_image );
bool DumpSettings( char *output, bool verbose ); bool DumpSettings( char *output, bool verbose );
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon ); static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ); static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
static int Load( Monitor *monitor, Zone **&zones ); static int Load( Monitor *monitor, Zone **&zones );
//================================================= //=================================================
bool CheckOverloadCount(); bool CheckOverloadCount();
int GetOverloadCount(); int GetOverloadCount();
void SetOverloadCount(int nOverCount); void SetOverloadCount(int nOverCount);
int GetOverloadFrames(); int GetOverloadFrames();
//================================================= //=================================================
bool CheckExtendAlarmCount(); bool CheckExtendAlarmCount();
int GetExtendAlarmCount(); int GetExtendAlarmCount();
void SetExtendAlarmCount(int nOverCount); void SetExtendAlarmCount(int nOverCount);
int GetExtendAlarmFrames(); int GetExtendAlarmFrames();
void SetScore(unsigned int nScore); void SetScore(unsigned int nScore);
void SetAlarmImage(const Image* srcImage); void SetAlarmImage(const Image* srcImage);
inline const Image *getPgImage() const { return( pg_image ); } inline const Image *getPgImage() const { return( pg_image ); }
inline const Range *getRanges() const { return( ranges ); } inline const Range *getRanges() const { return( ranges ); }
}; };

View File

@ -41,9 +41,9 @@ behind.
=head1 OPTIONS =head1 OPTIONS
-m, --monitor_id - ID of the monitor to analyse -m, --monitor_id - ID of the monitor to analyse
-h, --help - Display usage information -h, --help - Display usage information
-v, --version - Print the installed version of ZoneMinder -v, --version - Print the installed version of ZoneMinder
=cut =cut
@ -59,146 +59,146 @@ behind.
void Usage() void Usage()
{ {
fprintf( stderr, "zma -m <monitor_id>\n" ); fprintf( stderr, "zma -m <monitor_id>\n" );
fprintf( stderr, "Options:\n" ); fprintf( stderr, "Options:\n" );
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" ); fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
fprintf( stderr, " -h, --help : This screen\n" ); fprintf( stderr, " -h, --help : This screen\n" );
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
exit( 0 ); exit( 0 );
} }
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
self = argv[0]; self = argv[0];
srand( getpid() * time( 0 ) ); srand( getpid() * time( 0 ) );
int id = -1; int id = -1;
static struct option long_options[] = { static struct option long_options[] = {
{"monitor", 1, 0, 'm'}, {"monitor", 1, 0, 'm'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
while (1) while (1)
{ {
int option_index = 0; int option_index = 0;
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
if (c == -1) if (c == -1)
{ {
break; break;
} }
switch (c) switch (c)
{ {
case 'm': case 'm':
id = atoi(optarg); id = atoi(optarg);
break; break;
case 'h': case 'h':
case '?': case '?':
Usage(); Usage();
break; break;
case 'v': case 'v':
std::cout << ZM_VERSION << "\n"; std::cout << ZM_VERSION << "\n";
exit(0); exit(0);
default: default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break; break;
} }
} }
if (optind < argc) if (optind < argc)
{ {
fprintf( stderr, "Extraneous options, " ); fprintf( stderr, "Extraneous options, " );
while (optind < argc) while (optind < argc)
printf ("%s ", argv[optind++]); printf ("%s ", argv[optind++]);
printf ("\n"); printf ("\n");
Usage(); Usage();
} }
if ( id < 0 ) if ( id < 0 )
{ {
fprintf( stderr, "Bogus monitor %d\n", id ); fprintf( stderr, "Bogus monitor %d\n", id );
Usage(); Usage();
exit( 0 ); exit( 0 );
} }
char log_id_string[16]; char log_id_string[16];
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id ); snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
zmLoadConfig(); zmLoadConfig();
logInit( log_id_string ); logInit( log_id_string );
ssedetect(); ssedetect();
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS ); Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
if ( monitor ) if ( monitor )
{ {
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() ); Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
if ( config.opt_frame_server ) if ( config.opt_frame_server )
{ {
Event::OpenFrameSocket( monitor->Id() ); Event::OpenFrameSocket( monitor->Id() );
} }
zmSetDefaultHupHandler(); zmSetDefaultHupHandler();
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; sigset_t block_set;
sigemptyset( &block_set ); sigemptyset( &block_set );
useconds_t analysis_rate = monitor->GetAnalysisRate(); useconds_t analysis_rate = monitor->GetAnalysisRate();
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay(); unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
time_t last_analysis_update_time, cur_time; time_t last_analysis_update_time, cur_time;
monitor->UpdateAdaptiveSkip(); monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time( 0 ); last_analysis_update_time = time( 0 );
while( !zm_terminate ) while( !zm_terminate )
{ {
// Process the next image // Process the next image
sigprocmask( SIG_BLOCK, &block_set, 0 ); sigprocmask( SIG_BLOCK, &block_set, 0 );
// Some periodic updates are required for variable capturing framerate // Some periodic updates are required for variable capturing framerate
if ( analysis_update_delay ) if ( analysis_update_delay )
{ {
cur_time = time( 0 ); cur_time = time( 0 );
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay ) if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
{ {
analysis_rate = monitor->GetAnalysisRate(); analysis_rate = monitor->GetAnalysisRate();
monitor->UpdateAdaptiveSkip(); monitor->UpdateAdaptiveSkip();
last_analysis_update_time = cur_time; last_analysis_update_time = cur_time;
} }
} }
if ( !monitor->Analyse() ) if ( !monitor->Analyse() )
{ {
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE ); usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
} }
else if ( analysis_rate ) else if ( analysis_rate )
{ {
usleep( analysis_rate ); usleep( analysis_rate );
} }
if ( zm_reload ) if ( zm_reload )
{ {
monitor->Reload(); monitor->Reload();
zm_reload = false; zm_reload = false;
} }
sigprocmask( SIG_UNBLOCK, &block_set, 0 ); sigprocmask( SIG_UNBLOCK, &block_set, 0 );
} }
delete monitor; delete monitor;
} }
else else
{ {
fprintf( stderr, "Can't find monitor with id of %d\n", id ); fprintf( stderr, "Can't find monitor with id of %d\n", id );
} }
logTerm(); logTerm();
zmDbClose(); zmDbClose();
return( 0 ); return( 0 );
} }

View File

@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
=head1 OPTIONS =head1 OPTIONS
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc -d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
-r <proto> -H <host> -P <port> -p <path> - For remote cameras -r <proto> -H <host> -P <port> -p <path> - For remote cameras
-f, --file <file_path> - For local images, jpg file to access. -f, --file <file_path> - For local images, jpg file to access.
-m, --monitor_id - ID of the monitor to analyse -m, --monitor_id - ID of the monitor to analyse
-h, --help - Display usage information -h, --help - Display usage information
-v, --version - Print the installed version of ZoneMinder -v, --version - Print the installed version of ZoneMinder
=cut =cut
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
void Usage() void Usage()
{ {
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" ); fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
fprintf( stderr, "Options:\n" ); fprintf( stderr, "Options:\n" );
#if defined(BSD) #if defined(BSD)
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" ); fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
#else #else
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" ); fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
#endif #endif
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" ); fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" ); fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" ); fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
fprintf( stderr, " -h, --help : This screen\n" ); fprintf( stderr, " -h, --help : This screen\n" );
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
exit( 0 ); exit( 0 );
} }
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
self = argv[0]; self = argv[0];
srand( getpid() * time( 0 ) ); srand( getpid() * time( 0 ) );
const char *device = ""; const char *device = "";
const char *protocol = ""; const char *protocol = "";
const char *host = ""; const char *host = "";
const char *port = ""; const char *port = "";
const char *path = ""; const char *path = "";
const char *file = ""; const char *file = "";
int monitor_id = -1; int monitor_id = -1;
static struct option long_options[] = { static struct option long_options[] = {
{"device", 1, 0, 'd'}, {"device", 1, 0, 'd'},
{"protocol", 1, 0, 'r'}, {"protocol", 1, 0, 'r'},
{"host", 1, 0, 'H'}, {"host", 1, 0, 'H'},
{"port", 1, 0, 'P'}, {"port", 1, 0, 'P'},
{"path", 1, 0, 'p'}, {"path", 1, 0, 'p'},
{"file", 1, 0, 'f'}, {"file", 1, 0, 'f'},
{"monitor", 1, 0, 'm'}, {"monitor", 1, 0, 'm'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
while (1) while (1)
{ {
int option_index = 0; int option_index = 0;
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index); int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
if (c == -1) if (c == -1)
{ {
break; break;
} }
switch (c) switch (c)
{ {
case 'd': case 'd':
device = optarg; device = optarg;
break; break;
case 'H': case 'H':
host = optarg; host = optarg;
break; break;
case 'P': case 'P':
port = optarg; port = optarg;
break; break;
case 'p': case 'p':
path = optarg; path = optarg;
break; break;
case 'f': case 'f':
file = optarg; file = optarg;
break; break;
case 'm': case 'm':
monitor_id = atoi(optarg); monitor_id = atoi(optarg);
break; break;
case 'h': case 'h':
case '?': case '?':
Usage(); Usage();
break; break;
case 'v': case 'v':
std::cout << ZM_VERSION << "\n"; std::cout << ZM_VERSION << "\n";
exit(0); exit(0);
default: default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break; break;
} }
} }
if (optind < argc) if (optind < argc)
{ {
fprintf( stderr, "Extraneous options, " ); fprintf( stderr, "Extraneous options, " );
while (optind < argc) while (optind < argc)
printf ("%s ", argv[optind++]); printf ("%s ", argv[optind++]);
printf ("\n"); printf ("\n");
Usage(); Usage();
} }
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) ); int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
if ( modes > 1 ) if ( modes > 1 )
{ {
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" ); fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
Usage(); Usage();
exit( 0 ); exit( 0 );
} }
if ( modes < 1 ) if ( modes < 1 )
{ {
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" ); fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
Usage(); Usage();
exit( 0 ); exit( 0 );
} }
char log_id_string[32] = ""; char log_id_string[32] = "";
if ( device[0] ) if ( device[0] )
{ {
const char *slash_ptr = strrchr( device, '/' ); const char *slash_ptr = strrchr( device, '/' );
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device ); snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
} }
else if ( host[0] ) else if ( host[0] )
{ {
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host ); snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
} }
else if ( file[0] ) else if ( file[0] )
{ {
const char *slash_ptr = strrchr( file, '/' ); const char *slash_ptr = strrchr( file, '/' );
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file ); snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
} }
else else
{ {
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id ); snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
} }
zmLoadConfig(); zmLoadConfig();
logInit( log_id_string ); logInit( log_id_string );
ssedetect(); ssedetect();
Monitor **monitors = 0; Monitor **monitors = 0;
int n_monitors = 0; int n_monitors = 0;
#if ZM_HAS_V4L #if ZM_HAS_V4L
if ( device[0] ) if ( device[0] )
{ {
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE ); n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
} }
else else
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
if ( host[0] ) if ( host[0] )
{ {
if ( !port ) if ( !port )
port = "80"; port = "80";
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE ); n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
} }
else if ( file[0] ) else if ( file[0] )
{ {
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE ); n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
} }
else else
{ {
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE ); Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
if ( monitor ) if ( monitor )
{ {
monitors = new Monitor *[1]; monitors = new Monitor *[1];
monitors[0] = monitor; monitors[0] = monitor;
n_monitors = 1; n_monitors = 1;
} }
} }
if ( !n_monitors ) if ( !n_monitors )
{ {
Error( "No monitors found" ); Error( "No monitors found" );
exit ( -1 ); exit ( -1 );
} }
Info( "Starting Capture version %s", ZM_VERSION ); Info( "Starting Capture version %s", ZM_VERSION );
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; sigset_t block_set;
sigemptyset( &block_set ); sigemptyset( &block_set );
sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR1 );
sigaddset( &block_set, SIGUSR2 ); sigaddset( &block_set, SIGUSR2 );
if ( monitors[0]->PrimeCapture() < 0 ) if ( monitors[0]->PrimeCapture() < 0 )
{ {
Error( "Failed to prime capture of initial monitor" ); Error( "Failed to prime capture of initial monitor" );
exit( -1 ); exit( -1 );
} }
long *capture_delays = new long[n_monitors]; long *capture_delays = new long[n_monitors];
long *alarm_capture_delays = new long[n_monitors]; long *alarm_capture_delays = new long[n_monitors];
long *next_delays = new long[n_monitors]; long *next_delays = new long[n_monitors];
struct timeval * last_capture_times = new struct timeval[n_monitors]; struct timeval * last_capture_times = new struct timeval[n_monitors];
for ( int i = 0; i < n_monitors; i++ ) for ( int i = 0; i < n_monitors; i++ )
{ {
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
capture_delays[i] = monitors[i]->GetCaptureDelay(); capture_delays[i] = monitors[i]->GetCaptureDelay();
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
} }
int result = 0; int result = 0;
struct timeval now; struct timeval now;
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;
while( !zm_terminate ) while( !zm_terminate )
{ {
sigprocmask( SIG_BLOCK, &block_set, 0 ); sigprocmask( SIG_BLOCK, &block_set, 0 );
for ( int i = 0; i < n_monitors; i++ ) for ( int i = 0; i < n_monitors; i++ )
{ {
long min_delay = MAXINT; long min_delay = MAXINT;
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
for ( int j = 0; j < n_monitors; j++ ) for ( int j = 0; j < n_monitors; j++ )
{ {
if ( last_capture_times[j].tv_sec ) if ( last_capture_times[j].tv_sec )
{ {
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 ); DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
if ( monitors[i]->GetState() == Monitor::ALARM ) if ( monitors[i]->GetState() == Monitor::ALARM )
next_delays[j] = alarm_capture_delays[j]-delta_time.delta; next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
else else
next_delays[j] = capture_delays[j]-delta_time.delta; next_delays[j] = capture_delays[j]-delta_time.delta;
if ( next_delays[j] < 0 ) if ( next_delays[j] < 0 )
next_delays[j] = 0; next_delays[j] = 0;
} }
else else
{ {
next_delays[j] = 0; next_delays[j] = 0;
} }
if ( next_delays[j] <= min_delay ) if ( next_delays[j] <= min_delay )
{ {
min_delay = next_delays[j]; min_delay = next_delays[j];
} }
} }
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
{ {
if ( monitors[i]->PreCapture() < 0 ) if ( monitors[i]->PreCapture() < 0 )
{ {
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
zm_terminate = true; zm_terminate = true;
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->Capture() < 0 ) if ( monitors[i]->Capture() < 0 )
{ {
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
zm_terminate = true; zm_terminate = true;
result = -1; result = -1;
break; break;
} }
if ( monitors[i]->PostCapture() < 0 ) if ( monitors[i]->PostCapture() < 0 )
{ {
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
zm_terminate = true; zm_terminate = true;
result = -1; result = -1;
break; break;
} }
if ( next_delays[i] > 0 ) if ( next_delays[i] > 0 )
{ {
gettimeofday( &now, NULL ); gettimeofday( &now, NULL );
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 ); DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
long sleep_time = next_delays[i]-delta_time.delta; long sleep_time = next_delays[i]-delta_time.delta;
if ( sleep_time > 0 ) if ( sleep_time > 0 )
{ {
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) ); usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
} }
} }
gettimeofday( &(last_capture_times[i]), NULL ); gettimeofday( &(last_capture_times[i]), NULL );
} }
} }
sigprocmask( SIG_UNBLOCK, &block_set, 0 ); sigprocmask( SIG_UNBLOCK, &block_set, 0 );
} }
for ( int i = 0; i < n_monitors; i++ ) for ( int i = 0; i < n_monitors; i++ )
{ {
delete monitors[i]; delete monitors[i];
} }
delete [] monitors; delete [] monitors;
delete [] alarm_capture_delays; delete [] alarm_capture_delays;
delete [] capture_delays; delete [] capture_delays;
delete [] next_delays; delete [] next_delays;
delete [] last_capture_times; delete [] last_capture_times;
logTerm(); logTerm();
zmDbClose(); zmDbClose();
return( result ); return( result );
} }

View File

@ -42,9 +42,9 @@ them itself.
=head1 OPTIONS =head1 OPTIONS
-m, --monitor_id - ID of the monitor to use -m, --monitor_id - ID of the monitor to use
-h, --help - Display usage information -h, --help - Display usage information
-v, --version - Print the installed version of ZoneMinder -v, --version - Print the installed version of ZoneMinder
=cut =cut
@ -73,278 +73,278 @@ them itself.
int OpenSocket( int monitor_id ) int OpenSocket( int monitor_id )
{ {
int sd = socket( AF_UNIX, SOCK_STREAM, 0); int sd = socket( AF_UNIX, SOCK_STREAM, 0);
if ( sd < 0 ) if ( sd < 0 )
{ {
Error( "Can't create socket: %s", strerror(errno) ); Error( "Can't create socket: %s", strerror(errno) );
return( -1 ); return( -1 );
} }
char sock_path[PATH_MAX] = ""; char sock_path[PATH_MAX] = "";
snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id );
if ( unlink( sock_path ) < 0 ) if ( unlink( sock_path ) < 0 )
{ {
Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) );
} }
struct sockaddr_un addr; struct sockaddr_un addr;
strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) );
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 )
{ {
Error( "Can't bind: %s", strerror(errno) ); Error( "Can't bind: %s", strerror(errno) );
exit( -1 ); exit( -1 );
} }
if ( listen( sd, SOMAXCONN ) < 0 ) if ( listen( sd, SOMAXCONN ) < 0 )
{ {
Error( "Can't listen: %s", strerror(errno) ); Error( "Can't listen: %s", strerror(errno) );
return( -1 ); return( -1 );
} }
struct sockaddr_un rem_addr; struct sockaddr_un rem_addr;
socklen_t rem_addr_len = sizeof(rem_addr); socklen_t rem_addr_len = sizeof(rem_addr);
int new_sd = -1; int new_sd = -1;
if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 )
{ {
Error( "Can't accept: %s", strerror(errno) ); Error( "Can't accept: %s", strerror(errno) );
exit( -1 ); exit( -1 );
} }
close( sd ); close( sd );
sd = new_sd; sd = new_sd;
Info( "Frame server socket open, awaiting images" ); Info( "Frame server socket open, awaiting images" );
return( sd ); return( sd );
} }
int ReopenSocket( int &sd, int monitor_id ) int ReopenSocket( int &sd, int monitor_id )
{ {
close( sd ); close( sd );
return( sd = OpenSocket( monitor_id ) ); return( sd = OpenSocket( monitor_id ) );
} }
void Usage() void Usage()
{ {
fprintf( stderr, "zmf -m <monitor_id>\n" ); fprintf( stderr, "zmf -m <monitor_id>\n" );
fprintf( stderr, "Options:\n" ); fprintf( stderr, "Options:\n" );
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" ); fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
fprintf( stderr, " -h, --help : This screen\n" ); fprintf( stderr, " -h, --help : This screen\n" );
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
exit( 0 ); exit( 0 );
} }
int main( int argc, char *argv[] ) int main( int argc, char *argv[] )
{ {
self = argv[0]; self = argv[0];
srand( getpid() * time( 0 ) ); srand( getpid() * time( 0 ) );
int id = -1; int id = -1;
static struct option long_options[] = { static struct option long_options[] = {
{"monitor", 1, 0, 'm'}, {"monitor", 1, 0, 'm'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
while (1) while (1)
{ {
int option_index = 0; int option_index = 0;
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
if (c == -1) if (c == -1)
{ {
break; break;
} }
switch (c) switch (c)
{ {
case 'm': case 'm':
id = atoi(optarg); id = atoi(optarg);
break; break;
case 'h': case 'h':
case '?': case '?':
Usage(); Usage();
break; break;
case 'v': case 'v':
std::cout << ZM_VERSION << "\n"; std::cout << ZM_VERSION << "\n";
exit(0); exit(0);
default: default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break; break;
} }
} }
if (optind < argc) if (optind < argc)
{ {
fprintf( stderr, "Extraneous options, " ); fprintf( stderr, "Extraneous options, " );
while (optind < argc) while (optind < argc)
printf ("%s ", argv[optind++]); printf ("%s ", argv[optind++]);
printf ("\n"); printf ("\n");
Usage(); Usage();
} }
if ( id < 0 ) if ( id < 0 )
{ {
fprintf( stderr, "Bogus monitor %d\n", id ); fprintf( stderr, "Bogus monitor %d\n", id );
Usage(); Usage();
exit( 0 ); exit( 0 );
} }
char log_id_string[16]; char log_id_string[16];
snprintf( log_id_string, sizeof(log_id_string), "m%d", id ); snprintf( log_id_string, sizeof(log_id_string), "m%d", id );
zmLoadConfig(); zmLoadConfig();
logInit( "zmf" ); logInit( "zmf" );
ssedetect(); ssedetect();
Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY ); Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY );
if ( !monitor ) if ( !monitor )
{ {
fprintf( stderr, "Can't find monitor with id of %d\n", id ); fprintf( stderr, "Can't find monitor with id of %d\n", id );
exit( -1 ); exit( -1 );
} }
char capt_path[PATH_MAX]; char capt_path[PATH_MAX];
char anal_path[PATH_MAX]; char anal_path[PATH_MAX];
snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; sigset_t block_set;
sigemptyset( &block_set ); sigemptyset( &block_set );
int sd = OpenSocket( monitor->Id() ); int sd = OpenSocket( monitor->Id() );
FrameHeader frame_header = { 0, 0, false, 0 }; FrameHeader frame_header = { 0, 0, false, 0 };
//unsigned char *image_data = 0; //unsigned char *image_data = 0;
fd_set rfds; fd_set rfds;
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = 1; timeout.tv_sec = 1;
timeout.tv_usec = 0; timeout.tv_usec = 0;
while( 1 ) while( 1 )
{ {
struct timeval temp_timeout = timeout; struct timeval temp_timeout = timeout;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(sd, &rfds); FD_SET(sd, &rfds);
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
if( n_found == 0 ) if( n_found == 0 )
{ {
Debug( 1, "Select timed out" ); Debug( 1, "Select timed out" );
continue; continue;
} }
else if ( n_found < 0) else if ( n_found < 0)
{ {
Error( "Select error: %s", strerror(errno) ); Error( "Select error: %s", strerror(errno) );
ReopenSocket( sd, monitor->Id() ); ReopenSocket( sd, monitor->Id() );
continue; continue;
} }
sigprocmask( SIG_BLOCK, &block_set, 0 ); sigprocmask( SIG_BLOCK, &block_set, 0 );
int n_bytes = read( sd, &frame_header, sizeof(frame_header) ); int n_bytes = read( sd, &frame_header, sizeof(frame_header) );
if ( n_bytes != sizeof(frame_header) ) if ( n_bytes != sizeof(frame_header) )
{ {
if ( n_bytes < 0 ) if ( n_bytes < 0 )
{ {
Error( "Can't read frame header: %s", strerror(errno) ); Error( "Can't read frame header: %s", strerror(errno) );
} }
else if ( n_bytes > 0 ) else if ( n_bytes > 0 )
{ {
Error( "Incomplete read of frame header, %d bytes only", n_bytes ); Error( "Incomplete read of frame header, %d bytes only", n_bytes );
} }
else else
{ {
Warning( "Socket closed at remote end" ); Warning( "Socket closed at remote end" );
} }
ReopenSocket( sd, monitor->Id() ); ReopenSocket( sd, monitor->Id() );
continue; continue;
} }
Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length );
static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; static unsigned char image_data[ZM_MAX_IMAGE_SIZE];
// Read for pipe and loop until bytes expected have been read or an error occurs // Read for pipe and loop until bytes expected have been read or an error occurs
int bytes_read = 0; int bytes_read = 0;
do do
{ {
n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read );
if (n_bytes < 0) break; // break on error if (n_bytes < 0) break; // break on error
if (n_bytes < (int)frame_header.image_length) if (n_bytes < (int)frame_header.image_length)
{ {
// print some informational messages // print some informational messages
if (bytes_read == 0) if (bytes_read == 0)
{
Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
}
else if (bytes_read+n_bytes == (int)frame_header.image_length)
{
Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
}
else
{
Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
}
}
bytes_read+= n_bytes;
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
// Print errors if there was a problem
if ( n_bytes < 1 )
{ {
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
if ( n_bytes < 0 ) }
{ else if (bytes_read+n_bytes == (int)frame_header.image_length)
Error( "Can't read frame image data: %s", strerror(errno) );
}
else
{
Warning( "Socket closed at remote end" );
}
ReopenSocket( sd, monitor->Id() );
continue;
}
static char subpath[PATH_MAX] = "";
if ( config.use_deep_storage )
{ {
struct tm *time = localtime( &frame_header.event_time ); Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
} }
else else
{ {
snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
} }
}
bytes_read+= n_bytes;
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
static char path[PATH_MAX] = ""; // Print errors if there was a problem
snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); if ( n_bytes < 1 )
Debug( 1, "Got image, writing to %s", path ); {
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length);
if ( n_bytes < 0 )
{
Error( "Can't read frame image data: %s", strerror(errno) );
}
else
{
Warning( "Socket closed at remote end" );
}
ReopenSocket( sd, monitor->Id() );
continue;
}
FILE *fd = 0; static char subpath[PATH_MAX] = "";
if ( (fd = fopen( path, "w" )) < 0 ) if ( config.use_deep_storage )
{ {
Error( "Can't fopen '%s': %s", path, strerror(errno) ); struct tm *time = localtime( &frame_header.event_time );
exit( -1 ); snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
} }
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) else
{ {
Error( "Can't fwrite image data: %s", strerror(errno) ); snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id );
exit( -1 ); }
}
fclose( fd );
sigprocmask( SIG_UNBLOCK, &block_set, 0 ); static char path[PATH_MAX] = "";
} snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id );
logTerm(); Debug( 1, "Got image, writing to %s", path );
zmDbClose();
FILE *fd = 0;
if ( (fd = fopen( path, "w" )) < 0 )
{
Error( "Can't fopen '%s': %s", path, strerror(errno) );
exit( -1 );
}
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) )
{
Error( "Can't fwrite image data: %s", strerror(errno) );
exit( -1 );
}
fclose( fd );
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
}
logTerm();
zmDbClose();
} }

View File

@ -22,11 +22,11 @@
struct FrameHeader struct FrameHeader
{ {
unsigned long event_id; unsigned long event_id;
time_t event_time; time_t event_time;
unsigned long frame_id; unsigned long frame_id;
bool alarm_frame; bool alarm_frame;
unsigned long image_length; unsigned long image_length;
}; };
#endif // ZMFILE_H #endif // ZMFILE_H

View File

@ -28,318 +28,318 @@
bool ValidateAccess( User *user, int mon_id ) bool ValidateAccess( User *user, int mon_id )
{ {
bool allowed = true; bool allowed = true;
if ( mon_id > 0 ) if ( mon_id > 0 )
{ {
if ( user->getStream() < User::PERM_VIEW ) if ( user->getStream() < User::PERM_VIEW )
allowed = false; allowed = false;
if ( !user->canAccess( mon_id ) ) if ( !user->canAccess( mon_id ) )
allowed = false; allowed = false;
} }
else else
{ {
if ( user->getEvents() < User::PERM_VIEW ) if ( user->getEvents() < User::PERM_VIEW )
allowed = false; allowed = false;
} }
if ( !allowed ) if ( !allowed )
{ {
Error( "Error, insufficient privileges for requested action" ); Error( "Error, insufficient privileges for requested action" );
exit( -1 ); exit( -1 );
} }
return( allowed ); return( allowed );
} }
int main( int argc, const char *argv[] ) int main( int argc, const char *argv[] )
{ {
self = argv[0]; self = argv[0];
srand( getpid() * time( 0 ) ); srand( getpid() * time( 0 ) );
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
char format[32] = ""; char format[32] = "";
int monitor_id = 0; int monitor_id = 0;
time_t event_time = 0; time_t event_time = 0;
int event_id = 0; int event_id = 0;
unsigned int frame_id = 1; unsigned int frame_id = 1;
unsigned int scale = 100; unsigned int scale = 100;
unsigned int rate = 100; unsigned int rate = 100;
double maxfps = 10.0; double maxfps = 10.0;
unsigned int bitrate = 100000; unsigned int bitrate = 100000;
unsigned int ttl = 0; unsigned int ttl = 0;
EventStream::StreamMode replay = EventStream::MODE_SINGLE; EventStream::StreamMode replay = EventStream::MODE_SINGLE;
char username[64] = ""; char username[64] = "";
char password[64] = ""; char password[64] = "";
char auth[64] = ""; char auth[64] = "";
unsigned int connkey = 0; unsigned int connkey = 0;
unsigned int playback_buffer = 0; unsigned int playback_buffer = 0;
bool nph = false; bool nph = false;
const char *basename = strrchr( argv[0], '/' ); const char *basename = strrchr( argv[0], '/' );
if (basename) //if we found a / lets skip past it if (basename) //if we found a / lets skip past it
basename++; basename++;
else //argv[0] will not always contain the full path, but rather just the script name else //argv[0] will not always contain the full path, but rather just the script name
basename = argv[0]; basename = argv[0];
const char *nph_prefix = "nph-"; const char *nph_prefix = "nph-";
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
{ {
nph = true; nph = true;
} }
zmLoadConfig(); zmLoadConfig();
logInit( "zms" ); logInit( "zms" );
ssedetect(); ssedetect();
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
const char *query = getenv( "QUERY_STRING" ); const char *query = getenv( "QUERY_STRING" );
if ( query ) if ( query )
{ {
Debug( 1, "Query: %s", query ); Debug( 1, "Query: %s", query );
char temp_query[1024]; char temp_query[1024];
strncpy( temp_query, query, sizeof(temp_query) ); strncpy( temp_query, query, sizeof(temp_query) );
char *q_ptr = temp_query; char *q_ptr = temp_query;
char *parms[16]; // Shouldn't be more than this char *parms[16]; // Shouldn't be more than this
int parm_no = 0; int parm_no = 0;
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
{
parm_no++;
q_ptr = NULL;
}
for ( int p = 0; p < parm_no; p++ )
{
char *name = strtok( parms[p], "=" );
char *value = strtok( NULL, "=" );
if ( !value )
value = (char *)"";
if ( !strcmp( name, "source" ) )
{
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
}
else if ( !strcmp( name, "mode" ) )
{
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
}
else if ( !strcmp( name, "format" ) )
strncpy( format, value, sizeof(format) );
else if ( !strcmp( name, "monitor" ) )
monitor_id = atoi( value );
else if ( !strcmp( name, "time" ) )
event_time = atoi( value );
else if ( !strcmp( name, "event" ) )
event_id = strtoull( value, (char **)NULL, 10 );
else if ( !strcmp( name, "frame" ) )
frame_id = strtoull( value, (char **)NULL, 10 );
else if ( !strcmp( name, "scale" ) )
scale = atoi( value );
else if ( !strcmp( name, "rate" ) )
rate = atoi( value );
else if ( !strcmp( name, "maxfps" ) )
maxfps = atof( value );
else if ( !strcmp( name, "bitrate" ) )
bitrate = atoi( value );
else if ( !strcmp( name, "ttl" ) )
ttl = atoi(value);
else if ( !strcmp( name, "replay" ) )
{
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
}
else if ( !strcmp( name, "connkey" ) )
connkey = atoi(value);
else if ( !strcmp( name, "buffer" ) )
playback_buffer = atoi(value);
else if ( config.opt_use_auth )
{
if ( strcmp( config.auth_relay, "none" ) == 0 )
{
if ( !strcmp( name, "user" ) )
{
strncpy( username, value, sizeof(username) );
}
}
else
{
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{
if ( !strcmp( name, "auth" ) )
{
strncpy( auth, value, sizeof(auth) );
}
}
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{
if ( !strcmp( name, "user" ) )
{
strncpy( username, value, sizeof(username) );
}
if ( !strcmp( name, "pass" ) )
{
strncpy( password, value, sizeof(password) );
}
}
}
}
}
}
if ( config.opt_use_auth )
{
User *user = 0;
if ( strcmp( config.auth_relay, "none" ) == 0 )
{
if ( *username )
{
user = zmLoadUser( username );
}
}
else
{
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{
if ( *auth )
{
user = zmLoadAuthUser( auth, config.auth_hash_ips );
}
}
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{
if ( *username && *password )
{
user = zmLoadUser( username, password );
}
}
}
if ( !user )
{
Error( "Unable to authenticate user" );
logTerm();
zmDbClose();
return( -1 );
}
ValidateAccess( user, monitor_id );
}
setbuf( stdout, 0 );
if ( nph )
{
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
}
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
time_t now = time( 0 );
char date_string[64];
strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" );
fprintf( stdout, "Last-Modified: %s\r\n", date_string );
fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" );
fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" );
fprintf( stdout, "Pragma: no-cache\r\n");
// Removed as causing more problems than it fixed.
//if ( !nph )
//{
//fprintf( stdout, "Content-Length: 0\r\n");
//}
if ( source == ZMS_MONITOR )
{
MonitorStream stream;
stream.setStreamScale( scale );
stream.setStreamReplayRate( rate );
stream.setStreamMaxFPS( maxfps );
stream.setStreamTTL( ttl );
stream.setStreamQueue( connkey );
stream.setStreamBuffer( playback_buffer );
if ( ! stream.setStreamStart( monitor_id ) ) {
Error( "Unable to connect to zmc process for monitor %d", monitor_id );
fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
logTerm();
zmDbClose();
return( -1 );
}
if ( mode == ZMS_JPEG )
{
stream.setStreamType( MonitorStream::STREAM_JPEG );
}
else if ( mode == ZMS_RAW )
{
stream.setStreamType( MonitorStream::STREAM_RAW );
}
else if ( mode == ZMS_ZIP )
{
stream.setStreamType( MonitorStream::STREAM_ZIP );
}
else if ( mode == ZMS_SINGLE )
{
stream.setStreamType( MonitorStream::STREAM_SINGLE );
}
else
{
#if HAVE_LIBAVCODEC
stream.setStreamFormat( format );
stream.setStreamBitrate( bitrate );
stream.setStreamType( MonitorStream::STREAM_MPEG );
#else // HAVE_LIBAVCODEC
Error( "MPEG streaming of '%s' attempted while disabled", query );
fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" );
logTerm();
zmDbClose();
return( -1 );
#endif // HAVE_LIBAVCODEC
}
stream.runStream();
}
else if ( source == ZMS_EVENT )
{ {
EventStream stream; parm_no++;
stream.setStreamScale( scale ); q_ptr = NULL;
stream.setStreamReplayRate( rate );
stream.setStreamMaxFPS( maxfps );
stream.setStreamMode( replay );
stream.setStreamQueue( connkey );
if ( monitor_id && event_time )
{
stream.setStreamStart( monitor_id, event_time );
}
else
{
stream.setStreamStart( event_id, frame_id );
}
if ( mode == ZMS_JPEG )
{
stream.setStreamType( EventStream::STREAM_JPEG );
}
else
{
#if HAVE_LIBAVCODEC
stream.setStreamFormat( format );
stream.setStreamBitrate( bitrate );
stream.setStreamType( EventStream::STREAM_MPEG );
#else // HAVE_LIBAVCODEC
Error( "MPEG streaming of '%s' attempted while disabled", query );
fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" );
logTerm();
zmDbClose();
return( -1 );
#endif // HAVE_LIBAVCODEC
}
stream.runStream();
} }
for ( int p = 0; p < parm_no; p++ )
{
char *name = strtok( parms[p], "=" );
char *value = strtok( NULL, "=" );
if ( !value )
value = (char *)"";
if ( !strcmp( name, "source" ) )
{
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
}
else if ( !strcmp( name, "mode" ) )
{
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
}
else if ( !strcmp( name, "format" ) )
strncpy( format, value, sizeof(format) );
else if ( !strcmp( name, "monitor" ) )
monitor_id = atoi( value );
else if ( !strcmp( name, "time" ) )
event_time = atoi( value );
else if ( !strcmp( name, "event" ) )
event_id = strtoull( value, (char **)NULL, 10 );
else if ( !strcmp( name, "frame" ) )
frame_id = strtoull( value, (char **)NULL, 10 );
else if ( !strcmp( name, "scale" ) )
scale = atoi( value );
else if ( !strcmp( name, "rate" ) )
rate = atoi( value );
else if ( !strcmp( name, "maxfps" ) )
maxfps = atof( value );
else if ( !strcmp( name, "bitrate" ) )
bitrate = atoi( value );
else if ( !strcmp( name, "ttl" ) )
ttl = atoi(value);
else if ( !strcmp( name, "replay" ) )
{
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
}
else if ( !strcmp( name, "connkey" ) )
connkey = atoi(value);
else if ( !strcmp( name, "buffer" ) )
playback_buffer = atoi(value);
else if ( config.opt_use_auth )
{
if ( strcmp( config.auth_relay, "none" ) == 0 )
{
if ( !strcmp( name, "user" ) )
{
strncpy( username, value, sizeof(username) );
}
}
else
{
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{
if ( !strcmp( name, "auth" ) )
{
strncpy( auth, value, sizeof(auth) );
}
}
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{
if ( !strcmp( name, "user" ) )
{
strncpy( username, value, sizeof(username) );
}
if ( !strcmp( name, "pass" ) )
{
strncpy( password, value, sizeof(password) );
}
}
}
}
}
}
logTerm(); if ( config.opt_use_auth )
zmDbClose(); {
User *user = 0;
return( 0 ); if ( strcmp( config.auth_relay, "none" ) == 0 )
{
if ( *username )
{
user = zmLoadUser( username );
}
}
else
{
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
{
if ( *auth )
{
user = zmLoadAuthUser( auth, config.auth_hash_ips );
}
}
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
{
if ( *username && *password )
{
user = zmLoadUser( username, password );
}
}
}
if ( !user )
{
Error( "Unable to authenticate user" );
logTerm();
zmDbClose();
return( -1 );
}
ValidateAccess( user, monitor_id );
}
setbuf( stdout, 0 );
if ( nph )
{
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
}
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
time_t now = time( 0 );
char date_string[64];
strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" );
fprintf( stdout, "Last-Modified: %s\r\n", date_string );
fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" );
fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" );
fprintf( stdout, "Pragma: no-cache\r\n");
// Removed as causing more problems than it fixed.
//if ( !nph )
//{
//fprintf( stdout, "Content-Length: 0\r\n");
//}
if ( source == ZMS_MONITOR )
{
MonitorStream stream;
stream.setStreamScale( scale );
stream.setStreamReplayRate( rate );
stream.setStreamMaxFPS( maxfps );
stream.setStreamTTL( ttl );
stream.setStreamQueue( connkey );
stream.setStreamBuffer( playback_buffer );
if ( ! stream.setStreamStart( monitor_id ) ) {
Error( "Unable to connect to zmc process for monitor %d", monitor_id );
fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
logTerm();
zmDbClose();
return( -1 );
}
if ( mode == ZMS_JPEG )
{
stream.setStreamType( MonitorStream::STREAM_JPEG );
}
else if ( mode == ZMS_RAW )
{
stream.setStreamType( MonitorStream::STREAM_RAW );
}
else if ( mode == ZMS_ZIP )
{
stream.setStreamType( MonitorStream::STREAM_ZIP );
}
else if ( mode == ZMS_SINGLE )
{
stream.setStreamType( MonitorStream::STREAM_SINGLE );
}
else
{
#if HAVE_LIBAVCODEC
stream.setStreamFormat( format );
stream.setStreamBitrate( bitrate );
stream.setStreamType( MonitorStream::STREAM_MPEG );
#else // HAVE_LIBAVCODEC
Error( "MPEG streaming of '%s' attempted while disabled", query );
fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" );
logTerm();
zmDbClose();
return( -1 );
#endif // HAVE_LIBAVCODEC
}
stream.runStream();
}
else if ( source == ZMS_EVENT )
{
EventStream stream;
stream.setStreamScale( scale );
stream.setStreamReplayRate( rate );
stream.setStreamMaxFPS( maxfps );
stream.setStreamMode( replay );
stream.setStreamQueue( connkey );
if ( monitor_id && event_time )
{
stream.setStreamStart( monitor_id, event_time );
}
else
{
stream.setStreamStart( event_id, frame_id );
}
if ( mode == ZMS_JPEG )
{
stream.setStreamType( EventStream::STREAM_JPEG );
}
else
{
#if HAVE_LIBAVCODEC
stream.setStreamFormat( format );
stream.setStreamBitrate( bitrate );
stream.setStreamType( EventStream::STREAM_MPEG );
#else // HAVE_LIBAVCODEC
Error( "MPEG streaming of '%s' attempted while disabled", query );
fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" );
logTerm();
zmDbClose();
return( -1 );
#endif // HAVE_LIBAVCODEC
}
stream.runStream();
}
logTerm();
zmDbClose();
return( 0 );
} }

252
src/zmstreamer.cpp Normal file
View File

@ -0,0 +1,252 @@
//
// ZoneMinder Streamer, $Date: 2010-10-14 23:21:00 +0200 (Thu, 14 Oct 2010) $
// Copyright (C) 2001-2010 Philip Coombes, Chris Kistner
//
// This program is based on revision 3143 of
// http://svn.zoneminder.com/svn/zm/trunk/src/zms.cpp
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
/*
=head1 NAME
zmstreamer - eyeZM video streamer
=head1 SYNOPSIS
zmstreamer -e <mode>
zmstreamer -o <format>
zmstreamer -u <buffer size>
zmstreamer -f <maximum fps>
zmstreamer -s <scale>
zmstreamer -b <bitrate in bps>
zmstreamer -m <monitor id>
zmstreamer -d <debug mode>
zmstreamer -i
zmstreamer -?
zmstreamer -h
zmstreamer -v
=head1 DESCRIPTION
*DEPRECIATED* The xml skin and all files associated with the xml skin are now
depreciated. Please use the ZoneMinder API instead.
This binary works in conjunction with the XML skin to stream video to iPhones
running the eyeZm app.
=head1 OPTIONS
-e <mode> - Specify output mode: mpeg/jpg/zip/single/raw.
-o <format> - Specify output format.
-u <buffer size> - Specify buffer size in ms.
-f <maximum fps> - Specify maximum framerate.
-s <scale> - Specify scale.
-b <bitrate in bps> - Specify bitrate.
-m <monitor id> - Specify monitor id.
-d <debug mode> - 0 = off, 1 = no streaming, 2 = with streaming.
-i, -?, -h - Display usage information
-v - Print the installed version of ZoneMinder
=cut
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "zm.h"
#include "zm_db.h"
#include "zm_user.h"
#include "zm_signal.h"
#include "zm_monitor.h"
#include "zm_stream.h"
// Possible command-line options
#define OPTIONS "e:o:u:f:s:b:m:d:i:?:h:v"
// Default ZMS values
#define ZMS_DEFAULT_DEBUG 0
#define ZMS_DEFAULT_ID 1
#define ZMS_DEFAULT_BITRATE 100000
#define ZMS_DEFAULT_SCALE 100
#define ZMS_DEFAULT_MODE "mpeg"
#define ZMS_DEFAULT_FORMAT "asf"
#define ZMS_DEFAULT_FPS 25.0
#define ZMS_DEFAULT_BUFFER 1000
int main(int argc, char** argv) {
self = argv[0];
// Set initial values to the default values
int debug = ZMS_DEFAULT_DEBUG;
int id = ZMS_DEFAULT_ID;
int bitrate = ZMS_DEFAULT_BITRATE;
int scale = ZMS_DEFAULT_SCALE;
char mode[32];
sprintf(mode, "%s", ZMS_DEFAULT_MODE);
char format[32];
sprintf(format, "%s", ZMS_DEFAULT_FORMAT);
double maxfps = ZMS_DEFAULT_FPS;
int buffer = ZMS_DEFAULT_BUFFER;
// Parse command-line options
int arg;
while ((arg = getopt(argc, argv, OPTIONS)) != -1) {
switch (arg) {
case 'e':
sprintf(mode, "%s", optarg);
break;
case 'o':
sprintf(format, "%s", optarg);
break;
case 'u':
buffer = atoi(optarg);
break;
case 'f':
maxfps = atof(optarg);
break;
case 's':
scale = atoi(optarg);
break;
case 'b':
bitrate = atoi(optarg);
break;
case 'm':
id = atoi(optarg);
break;
case 'd':
debug = atoi(optarg);
break;
case 'h':
case 'i':
case '?':
printf("-e <mode> : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE);
printf("-o <format> : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT);
printf("-u <buffer size> : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER);
printf("-f <maximum fps> : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS);
printf("-s <scale> : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE);
printf("-b <bitrate in bps> : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE);
printf("-m <monitor id> : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID);
printf("-d <debug mode> : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n");
printf("-i or -? or -h: This information\n");
printf("-v : This installed version of ZoneMinder\n");
return EXIT_SUCCESS;
case 'v':
std::cout << ZM_VERSION << "\n";
exit(0);
}
}
// Set stream type
StreamBase::StreamType streamtype;
if (!strcasecmp("raw", mode))
streamtype = MonitorStream::STREAM_RAW;
else if (!strcasecmp("mpeg", mode))
streamtype = MonitorStream::STREAM_MPEG;
else if (!strcasecmp("jpg", mode))
streamtype = MonitorStream::STREAM_JPEG;
else if (!strcasecmp("single", mode))
streamtype = MonitorStream::STREAM_SINGLE;
else if (!strcasecmp("zip", mode))
streamtype = MonitorStream::STREAM_ZIP;
else
streamtype = MonitorStream::STREAM_MPEG;
if (debug) {
// Show stream parameters
printf("Stream parameters:\n");
switch (streamtype) {
case MonitorStream::STREAM_MPEG:
printf("Output mode (-e) = %s\n", "mpeg");
printf("Output format (-o) = %s\n", format);
break;
default:
printf("Output mode (-e) = %s\n", mode);
}
printf("Buffer size (-u) = %d ms\n", buffer);
printf("Maximum FPS (-f) = %lf FPS\n", maxfps);
printf("Scale (-s) = %d%%\n", scale);
printf("Bitrate (-b) = %d bps\n", bitrate);
printf("Monitor Id (-m) = %d\n", id);
}
if (debug) {
// Set ZM debugger to print to stdout
printf("Setting up ZoneMinder debugger to print to stdout...");
setenv("ZM_DBG_PRINT", "1", 1);
printf("Done.\n");
}
// Loading ZM configurations
printf("Loading ZoneMinder configurations...");
zmLoadConfig();
printf("Done.\n");
logInit("zmstreamer");
ssedetect();
// Setting stream parameters
MonitorStream stream;
stream.setStreamScale(scale); // default = 100 (scale)
stream.setStreamReplayRate(100); // default = 100 (rate)
stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps)
if (debug) stream.setStreamTTL(1);
else stream.setStreamTTL(0); // default = 0 (ttl)
stream.setStreamQueue(0); // default = 0 (connkey)
stream.setStreamBuffer(buffer); // default = 0 (buffer)
stream.setStreamStart(id); // default = 0 (monitor_id)
stream.setStreamType(streamtype);
if (streamtype == MonitorStream::STREAM_MPEG) {
#if HAVE_LIBAVCODEC
if (debug) printf("HAVE_LIBAVCODEC is set\n");
stream.setStreamFormat(format); // default = "" (format)
stream.setStreamBitrate(bitrate); // default = 100000 (bitrate)
#else
fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n");
logTerm();
zmDbClose();
return EXIT_FAILURE;
#endif
}
if (debug != 1) {
if (debug) printf("Running stream...");
// Output headers
fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION);
time_t now = time(0);
char date_string[64];
strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n");
fprintf(stdout, "Last-Modified: %s\r\n", date_string);
fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n");
fprintf(stdout, "Pragma: no-cache\r\n");
// Run stream
stream.runStream();
}
if (debug) printf("Done.\n");
logTerm();
zmDbClose();
return (EXIT_SUCCESS);
}

File diff suppressed because it is too large Load Diff