Merge branch 'master' into storageareas
This commit is contained in:
commit
af66105c37
4
src/zm.h
4
src/zm.h
|
@ -26,8 +26,8 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#ifdef SOLARIS
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#endif
|
||||
#include "zm_logger.h"
|
||||
|
||||
|
|
12296
src/zm_bigfont.h
12296
src/zm_bigfont.h
File diff suppressed because it is too large
Load Diff
58
src/zm_box.h
58
src/zm_box.h
|
@ -36,39 +36,39 @@
|
|||
class Box
|
||||
{
|
||||
private:
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
|
||||
public:
|
||||
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_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( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
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_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( 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 int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_BOX_H
|
||||
|
|
|
@ -25,57 +25,57 @@
|
|||
|
||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
unsigned int Buffer::expand( unsigned int count )
|
||||
{
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
return( mSize );
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
|
332
src/zm_buffer.h
332
src/zm_buffer.h
|
@ -27,183 +27,183 @@
|
|||
class Buffer
|
||||
{
|
||||
protected:
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
//unsigned int Allocation() const { return( mAllocation ); }
|
||||
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;
|
||||
}
|
||||
|
||||
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 )
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
memcpy( mStorage, mHead, 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
|
||||
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;
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
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 );
|
||||
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
|
||||
|
|
798
src/zm_comms.cpp
798
src/zm_comms.cpp
File diff suppressed because it is too large
Load Diff
1284
src/zm_comms.h
1284
src/zm_comms.h
File diff suppressed because it is too large
Load Diff
|
@ -25,48 +25,48 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
|
||||
#ifdef HAVE_LIBAVFORMAT
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
#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_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_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||
#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_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_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_RATE_BASE 100 // The factor by which we bump up 'rate' 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_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_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||
#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_MED_BUFSIZ 1024 // 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_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_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_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||
|
||||
extern void zmLoadConfig();
|
||||
|
||||
struct StaticConfig
|
||||
{
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
};
|
||||
|
||||
extern StaticConfig staticConfig;
|
||||
|
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
|
|||
class ConfigItem
|
||||
{
|
||||
private:
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
|
||||
public:
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
};
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
ZM_CFG_DECLARE_LIST
|
||||
ZM_CFG_DECLARE_LIST
|
||||
|
||||
private:
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
|
|
@ -28,40 +28,40 @@
|
|||
class Coord
|
||||
{
|
||||
private:
|
||||
int x, y;
|
||||
int x, y;
|
||||
|
||||
public:
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
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 int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
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 int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
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( !(operator<(coord)) ); }
|
||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||
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 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( x < coord.x && y < coord.y ); }
|
||||
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 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
|
||||
|
|
|
@ -27,42 +27,42 @@
|
|||
class Exception
|
||||
{
|
||||
protected:
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
|
||||
protected:
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
|
||||
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:
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_EXCEPTION_H
|
||||
|
|
6660
src/zm_font.h
6660
src/zm_font.h
File diff suppressed because it is too large
Load Diff
9090
src/zm_image.cpp
9090
src/zm_image.cpp
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
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)
|
||||
{
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImageAnalyser::~ImageAnalyser()
|
||||
{
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++It)
|
||||
delete *It;
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++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)
|
||||
{
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++It)
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++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);
|
||||
if (detect_score)
|
||||
{
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
return score;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,17 +23,17 @@ using namespace std;
|
|||
class ImageAnalyser {
|
||||
public:
|
||||
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
|
||||
private:
|
||||
|
||||
|
|
452
src/zm_jpeg.cpp
452
src/zm_jpeg.cpp
|
@ -32,65 +32,65 @@ static int jpeg_err_count = 0;
|
|||
|
||||
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||
}
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Expanded data destination object for memory */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
} mem_destination_mgr;
|
||||
|
||||
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
|
||||
|
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
|
|||
|
||||
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 */
|
||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
/* 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->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
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)
|
||||
{
|
||||
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 );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
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)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||
*(dest->outbuffer_size) += datacount;
|
||||
}
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, 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 )
|
||||
{
|
||||
mem_dest_ptr dest;
|
||||
mem_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* 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
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* 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
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* 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));
|
||||
}
|
||||
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
}
|
||||
|
||||
/* Expanded data source object for memory input */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
} mem_source_mgr;
|
||||
|
||||
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
|
||||
|
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
|
|||
|
||||
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,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
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)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if ( num_bytes > 0 )
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
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);
|
||||
/* 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;
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|||
|
||||
static void term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* no work necessary here */
|
||||
/* no work necessary here */
|
||||
}
|
||||
|
||||
|
||||
|
@ -354,113 +354,113 @@ static void term_source (j_decompress_ptr cinfo)
|
|||
|
||||
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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->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 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 ) {
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 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,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 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,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 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,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 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,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ extern "C"
|
|||
/* Stuff for overriden error handlers */
|
||||
struct zm_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
typedef struct zm_error_mgr *zm_error_ptr;
|
||||
|
|
298
src/zm_logger.h
298
src/zm_logger.h
|
@ -33,190 +33,190 @@
|
|||
class Logger
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
|
||||
typedef int Level;
|
||||
typedef int Level;
|
||||
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
int mSyslogLevel;
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
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 mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
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 void usrHandler( int sig );
|
||||
static bool smInitialised;
|
||||
static Logger *smInstance;
|
||||
|
||||
public:
|
||||
friend void logInit( const char *name, const Options &options );
|
||||
friend void logTerm();
|
||||
|
||||
static Logger *fetch()
|
||||
{
|
||||
if ( !smInstance )
|
||||
{
|
||||
smInstance = new Logger();
|
||||
Options options;
|
||||
smInstance->initialise( "undef", options );
|
||||
}
|
||||
return( smInstance );
|
||||
}
|
||||
static StringMap smCodes;
|
||||
static IntMap smSyslogPriorities;
|
||||
|
||||
private:
|
||||
Logger();
|
||||
~Logger();
|
||||
bool mInitialised;
|
||||
|
||||
public:
|
||||
void initialise( const std::string &id, const Options &options );
|
||||
void terminate();
|
||||
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 mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
|
||||
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();
|
||||
static void usrHandler( int sig );
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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 );
|
||||
return( smInstance );
|
||||
}
|
||||
|
||||
private:
|
||||
void logFile( const std::string &logFile );
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openSyslog();
|
||||
void closeSyslog();
|
||||
void closeDatabase();
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
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 logTerm();
|
||||
inline const std::string &logId()
|
||||
{
|
||||
return( Logger::fetch()->id() );
|
||||
return( Logger::fetch()->id() );
|
||||
}
|
||||
inline Logger::Level logLevel()
|
||||
{
|
||||
return( Logger::fetch()->level() );
|
||||
return( Logger::fetch()->level() );
|
||||
}
|
||||
inline void logCapLevel( Logger::Level level )
|
||||
{
|
||||
Logger::fetch()->level( level );
|
||||
Logger::fetch()->level( level );
|
||||
}
|
||||
inline Logger::Level logDebugging()
|
||||
{
|
||||
return( Logger::fetch()->debugOn() );
|
||||
return( Logger::fetch()->debugOn() );
|
||||
}
|
||||
|
||||
#define logPrintf(logLevel,params...) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
|
||||
#define logHexdump(logLevel,data,len) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
|
||||
/* Debug compiled out */
|
||||
#ifndef DBG_OFF
|
||||
|
@ -228,16 +228,16 @@ inline Logger::Level logDebugging()
|
|||
#endif
|
||||
|
||||
/* 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 Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#ifdef __GNUC__
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#else
|
||||
#define Enter(level)
|
||||
#define Exit(level)
|
||||
|
|
|
@ -24,138 +24,138 @@
|
|||
#include "zm.h"
|
||||
|
||||
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
||||
uint8_t* retptr;
|
||||
uint8_t* retptr;
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
|
||||
return retptr;
|
||||
return retptr;
|
||||
#else
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
|
||||
alloc = retptr + sizeof(void*);
|
||||
alloc = retptr + sizeof(void*);
|
||||
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
|
||||
return alloc;
|
||||
return alloc;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void zm_freealigned(void* ptr) {
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
#else
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
inline char *memstr( register const char *s, const char *n, size_t limit )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
register unsigned int i,j;
|
||||
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;
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
}
|
||||
|
||||
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
#endif // ZM_MEM_UTILS_H
|
||||
|
|
|
@ -27,60 +27,60 @@
|
|||
class VideoStream
|
||||
{
|
||||
protected:
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
|
||||
protected:
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
|
||||
protected:
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
|
||||
protected:
|
||||
static void Initialise();
|
||||
static void Initialise();
|
||||
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
|
||||
public:
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
140
src/zm_poly.cpp
140
src/zm_poly.cpp
|
@ -28,95 +28,95 @@
|
|||
|
||||
void Polygon::calcArea()
|
||||
{
|
||||
double float_area = 0.0L;
|
||||
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;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
double float_area = 0.0L;
|
||||
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;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
}
|
||||
|
||||
void Polygon::calcCentre()
|
||||
{
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
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_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_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
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_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_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, 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 )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
coords = new Coord[n_coords];
|
||||
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
calcCentre();
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
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 )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
}
|
||||
|
||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||
{
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
}
|
||||
|
||||
bool Polygon::isInside( const Coord &coord ) const
|
||||
{
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].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()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].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()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
}
|
||||
|
|
148
src/zm_poly.h
148
src/zm_poly.h
|
@ -33,93 +33,93 @@
|
|||
class Polygon
|
||||
{
|
||||
protected:
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
|
||||
protected:
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
|
||||
public:
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
};
|
||||
|
||||
#endif // ZM_POLY_H
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
}
|
||||
|
||||
RegExpr::~RegExpr()
|
||||
{
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
}
|
||||
|
||||
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 < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
if ( n_matches <= 0 )
|
||||
{
|
||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
}
|
||||
|
||||
const char *RegExpr::MatchString( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
}
|
||||
|
||||
int RegExpr::MatchLength( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
|
@ -35,29 +35,29 @@
|
|||
class RegExpr
|
||||
{
|
||||
protected:
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
|
||||
protected:
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
|
||||
protected:
|
||||
bool ok;
|
||||
bool ok;
|
||||
|
||||
public:
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
180
src/zm_rgb.h
180
src/zm_rgb.h
|
@ -20,80 +20,80 @@
|
|||
#ifndef 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_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
#define WHITE 0xff
|
||||
#define WHITE_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#define RGB_TRANSPARENT (0x01000000)
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#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 */
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* BGR or BGRA */
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* ARGB */
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
/* ABGR */
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
|
||||
/* 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) */
|
||||
/* 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_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_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>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_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)
|
||||
|
||||
/* ZM colours */
|
||||
#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. */
|
||||
/* 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 */
|
||||
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_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // ZM_RGB_H
|
||||
|
|
|
@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int consumed = 0;
|
||||
int consumed = 0;
|
||||
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
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 );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
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;
|
||||
}
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
break;
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
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);
|
||||
return( consumed );
|
||||
}
|
||||
|
||||
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 wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
mRtpSource.updateRtcpStats();
|
||||
mRtpSource.updateRtcpStats();
|
||||
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
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 wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
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;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
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 wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 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 )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::run()
|
||||
{
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
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 ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
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 ( !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;
|
||||
}
|
||||
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() );
|
||||
|
||||
// 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 )
|
||||
if ( nBytes )
|
||||
{
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
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() );
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( nBytes )
|
||||
{
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} 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" );
|
||||
}
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} 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" );
|
||||
}
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "zm_thread.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
|
||||
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
||||
|
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
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,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
// 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;
|
||||
|
||||
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;
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
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
|
||||
};
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// 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
|
||||
{
|
||||
// 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;
|
||||
};
|
||||
// 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:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
int recvPacket( 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 generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
int recvPacket( 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 generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_CTRL_H
|
||||
|
|
|
@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
||||
{
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
}
|
||||
|
||||
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;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
SockAddrInet localAddr;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
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" );
|
||||
mStop = true;
|
||||
break;
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
else
|
||||
{
|
||||
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 )
|
||||
{
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -30,16 +30,16 @@ class RtpSource;
|
|||
|
||||
struct RtpDataHeader
|
||||
{
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
};
|
||||
|
||||
class RtpDataThread : public Thread
|
||||
|
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_DATA_H
|
||||
|
|
|
@ -35,152 +35,152 @@ struct RtpDataHeader;
|
|||
class RtpSource
|
||||
{
|
||||
public:
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
private:
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
|
||||
private:
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
|
||||
_AVCODECID mCodecId;
|
||||
_AVCODECID mCodecId;
|
||||
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
|
||||
private:
|
||||
void init( uint16_t seq );
|
||||
void init( uint16_t seq );
|
||||
|
||||
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 );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &buffer );
|
||||
bool getFrame( Buffer &buffer );
|
||||
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
1508
src/zm_rtsp.cpp
1508
src/zm_rtsp.cpp
File diff suppressed because it is too large
Load Diff
152
src/zm_rtsp.h
152
src/zm_rtsp.h
|
@ -34,110 +34,110 @@
|
|||
class RtspThread : public Thread
|
||||
{
|
||||
public:
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
|
||||
private:
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
|
||||
private:
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
|
||||
private:
|
||||
int mId;
|
||||
int mId;
|
||||
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
|
||||
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
|
||||
SourceMap mSources;
|
||||
SourceMap mSources;
|
||||
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
|
||||
unsigned long mRtpTime;
|
||||
unsigned long mRtpTime;
|
||||
|
||||
bool mStop;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
public:
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTSP_H
|
||||
|
|
|
@ -28,206 +28,206 @@ namespace zm {
|
|||
|
||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
}
|
||||
|
||||
Authenticator::~Authenticator() {
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void Authenticator::reset() {
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
}
|
||||
|
||||
void Authenticator::authHandleHeader(std::string headerData)
|
||||
{
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
// Check if basic auth
|
||||
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;
|
||||
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++ )
|
||||
{
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
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() );
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
||||
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 result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
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 += 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 + "\"";
|
||||
}
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
#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
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
#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
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
#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
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
|
||||
return md5HexBuf;
|
||||
return md5HexBuf;
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
#endif // HAVE_DECL_MD5
|
||||
}
|
||||
|
||||
void Authenticator::checkAuthResponse(std::string &response) {
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zm
|
||||
|
|
|
@ -37,19 +37,19 @@ namespace zm {
|
|||
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
||||
class Authenticator {
|
||||
public:
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
private:
|
||||
std::string password() { return fPassword; }
|
||||
|
@ -61,7 +61,7 @@ private:
|
|||
std::string fUsername;
|
||||
std::string fPassword;
|
||||
std::string quote( std::string src );
|
||||
int nc;
|
||||
int nc;
|
||||
};
|
||||
|
||||
} // namespace zm
|
||||
|
|
824
src/zm_sdp.cpp
824
src/zm_sdp.cpp
|
@ -25,489 +25,489 @@
|
|||
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
};
|
||||
#else
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
};
|
||||
#endif
|
||||
|
||||
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
{
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
||||
mValue( 0 )
|
||||
mValue( 0 )
|
||||
{
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
{
|
||||
MediaDescriptor *currMedia = 0;
|
||||
MediaDescriptor *currMedia = 0;
|
||||
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
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;
|
||||
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 )
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
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' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
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' :
|
||||
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 )
|
||||
{
|
||||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
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" )
|
||||
{
|
||||
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 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 general SDP attribute '%s'", line.c_str() );
|
||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
{
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
}
|
||||
|
||||
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() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
*/
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
#else
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
#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 ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
#else
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
std::string codec_name;
|
||||
std::string codec_name;
|
||||
#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
|
||||
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 );
|
||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( 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
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
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
|
||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||
{
|
||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||
{
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( 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
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
348
src/zm_sdp.h
348
src/zm_sdp.h
|
@ -34,204 +34,204 @@
|
|||
class SessionDescriptor
|
||||
{
|
||||
protected:
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
_AVCODECID codecId;
|
||||
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
|
||||
public:
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
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;
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
std::string mSprops;
|
||||
|
||||
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:
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
};
|
||||
#if 0
|
||||
v=0
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||
#include <sys/sendfile.h>
|
||||
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);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
err = sendfile(out_fd, in_fd, offset, size);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
#elif HAVE_SENDFILE7_SUPPORT
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
return -EAGAIN;
|
||||
}
|
||||
#else
|
||||
#error "Your platform does not support sendfile. Sorry."
|
||||
|
|
|
@ -32,326 +32,326 @@
|
|||
StreamBase::~StreamBase()
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
#endif
|
||||
closeComms();
|
||||
closeComms();
|
||||
}
|
||||
|
||||
bool StreamBase::loadMonitor( int monitor_id )
|
||||
{
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool StreamBase::checkInitialised()
|
||||
{
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
|
||||
void StreamBase::updateFrameRate( double fps )
|
||||
{
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
}
|
||||
|
||||
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;
|
||||
memset( &msg, 0, sizeof(msg) );
|
||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||
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 );
|
||||
}
|
||||
if ( errno != EAGAIN )
|
||||
{
|
||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||
}
|
||||
}
|
||||
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 )
|
||||
{
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
|
||||
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 = ©_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 = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Crop( last_crop );
|
||||
}
|
||||
if ( !last_scale )
|
||||
last_scale = scale;
|
||||
if ( !last_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 = ©_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 = ©_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 )
|
||||
{
|
||||
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.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
#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->OpenStream();
|
||||
}
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
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->OpenStream();
|
||||
}
|
||||
else
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
}
|
||||
else
|
||||
#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];
|
||||
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 )
|
||||
{
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
|
||||
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) );
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamBase::closeComms()
|
||||
{
|
||||
if ( connkey > 0 )
|
||||
if ( connkey > 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
220
src/zm_stream.h
220
src/zm_stream.h
|
@ -33,149 +33,149 @@ class Monitor;
|
|||
class StreamBase
|
||||
{
|
||||
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:
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
|
||||
protected:
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
|
||||
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 { 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;
|
||||
|
||||
protected:
|
||||
Monitor *monitor;
|
||||
Monitor *monitor;
|
||||
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
|
||||
protected:
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
|
||||
protected:
|
||||
bool paused;
|
||||
int step;
|
||||
bool paused;
|
||||
int step;
|
||||
|
||||
struct timeval now;
|
||||
struct timeval now;
|
||||
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
VideoStream *vid_stream;
|
||||
VideoStream *vid_stream;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
||||
CmdMsg msg;
|
||||
CmdMsg msg;
|
||||
|
||||
protected:
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
|
||||
public:
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
vid_stream = 0;
|
||||
vid_stream = 0;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
};
|
||||
|
||||
#endif // ZM_STREAM_H
|
||||
|
|
|
@ -29,310 +29,310 @@
|
|||
|
||||
struct timespec getTimeout( int secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
struct timespec getTimeout( double secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock()
|
||||
{
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( int secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( double secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Mutex::locked()
|
||||
{
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
}
|
||||
|
||||
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::wait()
|
||||
{
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Condition::wait( int secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
bool Condition::wait( double secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
void Condition::signal()
|
||||
{
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::broadcast()
|
||||
{
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getValue() const
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> T ThreadData<T>::setValue( const T value )
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
Thread::Thread() :
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
{
|
||||
Debug( 1, "Creating thread" );
|
||||
Debug( 1, "Creating thread" );
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
}
|
||||
|
||||
void *Thread::mThreadFunc( void *arg )
|
||||
{
|
||||
Debug( 2, "Invoking thread" );
|
||||
Debug( 2, "Invoking thread" );
|
||||
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
thisPtr->status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
thisPtr->status = thisPtr->run();
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
||||
return (void *)&(thisPtr->status);
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
||||
return (void *)-1;
|
||||
}
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
thisPtr->status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
thisPtr->status = thisPtr->run();
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
||||
return (void *)&(thisPtr->status);
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
||||
return (void *)-1;
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::start()
|
||||
{
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
}
|
||||
|
||||
void Thread::join()
|
||||
{
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join already finished thread %d", mPid );
|
||||
}
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
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 )
|
||||
{
|
||||
pthread_kill( mThread, signal );
|
||||
pthread_kill( mThread, signal );
|
||||
}
|
||||
|
||||
// Some explicit template instantiations
|
||||
|
|
308
src/zm_thread.h
308
src/zm_thread.h
|
@ -36,27 +36,27 @@ class ThreadException : public Exception
|
|||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
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
|
||||
|
@ -64,215 +64,215 @@ class Mutex
|
|||
friend class Condition;
|
||||
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_mutex_t mMutex;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
private:
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
|
||||
public:
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
};
|
||||
|
||||
class ScopedMutex
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
Mutex &mMutex;
|
||||
|
||||
public:
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
|
||||
public:
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
};
|
||||
|
||||
class Semaphore : public Condition
|
||||
{
|
||||
private:
|
||||
Mutex mMutex;
|
||||
Mutex mMutex;
|
||||
|
||||
public:
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class ThreadData
|
||||
{
|
||||
private:
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
|
||||
public:
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
|
||||
protected:
|
||||
pthread_t mThread;
|
||||
pthread_t mThread;
|
||||
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
#ifndef SOLARIS
|
||||
pid_t mPid;
|
||||
pid_t mPid;
|
||||
#else
|
||||
pthread_t mPid;
|
||||
pthread_t mPid;
|
||||
#endif
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
int status; // Used in various funcions to get around return a local variable
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
int status; // Used in various funcions to get around return a local variable
|
||||
|
||||
protected:
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
#ifndef SOLARIS
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
#endif
|
||||
void exit( int p_status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&p_status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
void exit( int p_status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&p_status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
|
||||
public:
|
||||
virtual int run() = 0;
|
||||
virtual int run() = 0;
|
||||
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
};
|
||||
|
||||
#endif // ZM_THREAD_H
|
||||
|
|
188
src/zm_time.h
188
src/zm_time.h
|
@ -29,48 +29,48 @@
|
|||
|
||||
struct DeltaTimeval
|
||||
{
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
};
|
||||
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 10
|
||||
#define DT_PREC_1 DT_GRAN_10
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 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
|
||||
// for frames it will only usually be a fraction of a second or so
|
||||
#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))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (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))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
}
|
||||
|
||||
#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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
}
|
||||
|
||||
inline struct timeval tvZero()
|
||||
{
|
||||
struct timeval t = { 0, 0 };
|
||||
return( t );
|
||||
struct timeval t = { 0, 0 };
|
||||
return( 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 )
|
||||
{
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
}
|
||||
|
||||
inline struct timeval tvCheck( struct timeval &t )
|
||||
{
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
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_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
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_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
}
|
||||
|
||||
// Add t2 to t1
|
||||
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
}
|
||||
|
||||
// Subtract t2 from t1
|
||||
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
}
|
||||
|
||||
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
||||
{
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
}
|
||||
|
||||
#endif // ZM_TIME_H
|
||||
|
|
114
src/zm_timer.cpp
114
src/zm_timer.cpp
|
@ -24,96 +24,96 @@
|
|||
int Timer::TimerThread::mNextTimerId = 0;
|
||||
|
||||
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
Timer::TimerThread::~TimerThread()
|
||||
{
|
||||
cancel();
|
||||
cancel();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::cancel()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::reset()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
int Timer::TimerThread::run()
|
||||
{
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
} while ( mRepeat || (mReset && !timerExpired) );
|
||||
return( timerExpired );
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
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 )
|
||||
{
|
||||
mTimerThread.start();
|
||||
mTimerThread.start();
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
//cancel();
|
||||
//cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::cancel()
|
||||
{
|
||||
mTimerThread.cancel();
|
||||
mTimerThread.cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::reset()
|
||||
{
|
||||
mTimerThread.reset();
|
||||
mTimerThread.reset();
|
||||
}
|
||||
|
||||
|
|
120
src/zm_timer.h
120
src/zm_timer.h
|
@ -30,81 +30,81 @@
|
|||
class Timer
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class TimerThread : public Thread
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
class TimerThread : public Thread
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void cancel();
|
||||
void reset();
|
||||
int run();
|
||||
};
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
|
||||
protected:
|
||||
TimerThread mTimerThread;
|
||||
|
||||
protected:
|
||||
Timer( int timeout, bool repeat=false );
|
||||
|
||||
public:
|
||||
virtual ~Timer();
|
||||
|
||||
protected:
|
||||
virtual void expire()=0;
|
||||
|
||||
public:
|
||||
void cancel();
|
||||
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
|
||||
|
|
332
src/zm_user.cpp
332
src/zm_user.cpp
|
@ -29,119 +29,119 @@
|
|||
|
||||
User::User()
|
||||
{
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
}
|
||||
|
||||
User::User( MYSQL_ROW &dbrow )
|
||||
{
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
User::~User()
|
||||
{
|
||||
delete monitor_ids;
|
||||
delete monitor_ids;
|
||||
}
|
||||
|
||||
bool User::canAccess( int monitor_id )
|
||||
{
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Function to load a user from username and password
|
||||
// Please note that in auth relay mode = none, password is NULL
|
||||
User *zmLoadUser( const char *username, const char *password )
|
||||
{
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
char safer_password[129]; // current db password size is 64
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
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.
|
||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||
// 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 ) );
|
||||
|
||||
if ( 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 );
|
||||
} 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 );
|
||||
}
|
||||
if ( 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 );
|
||||
} 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 );
|
||||
}
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
|
||||
mysql_free_result( result );
|
||||
mysql_free_result( result );
|
||||
|
||||
return( user );
|
||||
return( user );
|
||||
}
|
||||
|
||||
// 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
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
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" );
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
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" );
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
|
||||
#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
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
#endif
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
#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
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
|
|
@ -39,33 +39,33 @@
|
|||
class User
|
||||
{
|
||||
public:
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
|
||||
protected:
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
|
||||
public:
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
};
|
||||
|
||||
User *zmLoadUser( const char *username, const char *password=0 );
|
||||
|
|
440
src/zm_utils.cpp
440
src/zm_utils.cpp
|
@ -28,256 +28,256 @@
|
|||
unsigned int sseversion = 0;
|
||||
|
||||
std::string trimSet(std::string str, std::string trimset) {
|
||||
// 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 endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
// 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 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(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
}
|
||||
|
||||
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) {
|
||||
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'
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
const std::string stringtf( const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
const std::string stringtf( const std::string &format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
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 stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
StringVector stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
break;
|
||||
}
|
||||
//Info( "Got new startIndex at %d", startIndex );
|
||||
} while ( startIndex != std::string::npos );
|
||||
//Info( "Finished with %d strings", stringVector.size() );
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
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 ) {
|
||||
std::stringstream ss;
|
||||
std::stringstream ss;
|
||||
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const std::string base64Encode( const std::string &inString )
|
||||
{
|
||||
static char base64_table[64] = { '\0' };
|
||||
static char base64_table[64] = { '\0' };
|
||||
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
}
|
||||
|
||||
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
|
||||
return items.size();
|
||||
return items.size();
|
||||
}
|
||||
|
||||
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets sse_version */
|
||||
void ssedetect() {
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
|
||||
__asm__ __volatile__(
|
||||
__asm__ __volatile__(
|
||||
#if defined(__i386__)
|
||||
"pushl %%ebx;\n\t"
|
||||
"pushl %%ebx;\n\t"
|
||||
#endif
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
#if defined(__i386__)
|
||||
"popl %%ebx;\n\t"
|
||||
"popl %%ebx;\n\t"
|
||||
#endif
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
#if !defined(__i386__)
|
||||
, "%ebx"
|
||||
, "%ebx"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
|
||||
#else
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
|
|||
#endif
|
||||
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(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
if(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
#else
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
#endif
|
||||
return dest;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
|
|||
|
||||
inline int max( int a, int b )
|
||||
{
|
||||
return( a>=b?a:b );
|
||||
return( a>=b?a:b );
|
||||
}
|
||||
|
||||
inline int min( int a, int b )
|
||||
{
|
||||
return( a<=b?a:b );
|
||||
return( a<=b?a:b );
|
||||
}
|
||||
|
||||
void ssedetect();
|
||||
|
|
226
src/zm_zone.h
226
src/zm_zone.h
|
@ -35,141 +35,141 @@ class Monitor;
|
|||
class Zone
|
||||
{
|
||||
protected:
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
|
||||
protected:
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
|
||||
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 std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||
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);
|
||||
|
||||
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 )
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
public:
|
||||
~Zone();
|
||||
~Zone();
|
||||
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
|
||||
};
|
||||
|
||||
|
|
238
src/zma.cpp
238
src/zma.cpp
|
@ -41,9 +41,9 @@ behind.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -59,146 +59,146 @@ behind.
|
|||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||
char log_id_string[16];
|
||||
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 )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
if ( monitor )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
}
|
||||
|
|
508
src/zmc.cpp
508
src/zmc.cpp
|
@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
=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
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
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)
|
||||
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
|
||||
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
|
||||
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, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
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, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
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 *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
const char *device = "";
|
||||
const char *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||
}
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
int n_monitors = 0;
|
||||
Monitor **monitors = 0;
|
||||
int n_monitors = 0;
|
||||
#if ZM_HAS_V4L
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
#endif // ZM_HAS_V4L
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
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 );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return( result );
|
||||
return( result );
|
||||
}
|
||||
|
|
10
src/zmf.h
10
src/zmf.h
|
@ -22,11 +22,11 @@
|
|||
|
||||
struct FrameHeader
|
||||
{
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
};
|
||||
|
||||
#endif // ZMFILE_H
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -1685,7 +1685,11 @@ function getDiskPercent()
|
|||
Error("disk_total_space returned false for " . ZM_DIR_EVENTS );
|
||||
return 0;
|
||||
}
|
||||
$space = round(($total - disk_free_space(ZM_DIR_EVENTS)) / $total * 100);
|
||||
$free = disk_free_space(ZM_DIR_EVENTS);
|
||||
if ( ! $free ) {
|
||||
Error("disk_free_space returned false for " . ZM_DIR_EVENTS );
|
||||
}
|
||||
$space = round(($total - $free) / $total * 100);
|
||||
return( $space );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue