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"
|
#include "zm_config.h"
|
||||||
#ifdef SOLARIS
|
#ifdef SOLARIS
|
||||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||||
#include <string.h> // define strerror() and friends
|
#include <string.h> // define strerror() and friends
|
||||||
#endif
|
#endif
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
|
|
||||||
|
|
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
|
class Box
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Coord lo, hi;
|
Coord lo, hi;
|
||||||
Coord size;
|
Coord size;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Box()
|
inline Box()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||||
|
|
||||||
inline const Coord &Lo() const { return( lo ); }
|
inline const Coord &Lo() const { return( lo ); }
|
||||||
inline int LoX() const { return( lo.X() ); }
|
inline int LoX() const { return( lo.X() ); }
|
||||||
inline int LoY() const { return( lo.Y() ); }
|
inline int LoY() const { return( lo.Y() ); }
|
||||||
inline const Coord &Hi() const { return( hi ); }
|
inline const Coord &Hi() const { return( hi ); }
|
||||||
inline int HiX() const { return( hi.X() ); }
|
inline int HiX() const { return( hi.X() ); }
|
||||||
inline int HiY() const { return( hi.Y() ); }
|
inline int HiY() const { return( hi.Y() ); }
|
||||||
inline const Coord &Size() const { return( size ); }
|
inline const Coord &Size() const { return( size ); }
|
||||||
inline int Width() const { return( size.X() ); }
|
inline int Width() const { return( size.X() ); }
|
||||||
inline int Height() const { return( size.Y() ); }
|
inline int Height() const { return( size.Y() ); }
|
||||||
inline int Area() const { return( size.X()*size.Y() ); }
|
inline int Area() const { return( size.X()*size.Y() ); }
|
||||||
|
|
||||||
inline const Coord Centre() const
|
inline const Coord Centre() const
|
||||||
{
|
{
|
||||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||||
return( Coord( mid_x, mid_y ) );
|
return( Coord( mid_x, mid_y ) );
|
||||||
}
|
}
|
||||||
inline bool Inside( const Coord &coord ) const
|
inline bool Inside( const Coord &coord ) const
|
||||||
{
|
{
|
||||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_BOX_H
|
#endif // ZM_BOX_H
|
||||||
|
|
|
@ -25,57 +25,57 @@
|
||||||
|
|
||||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||||
{
|
{
|
||||||
if ( mAllocation < pSize )
|
if ( mAllocation < pSize )
|
||||||
{
|
{
|
||||||
delete[] mStorage;
|
delete[] mStorage;
|
||||||
mAllocation = pSize;
|
mAllocation = pSize;
|
||||||
mHead = mStorage = new unsigned char[pSize];
|
mHead = mStorage = new unsigned char[pSize];
|
||||||
}
|
}
|
||||||
mSize = pSize;
|
mSize = pSize;
|
||||||
memcpy( mStorage, pStorage, mSize );
|
memcpy( mStorage, pStorage, mSize );
|
||||||
mHead = mStorage;
|
mHead = mStorage;
|
||||||
mTail = mHead + mSize;
|
mTail = mHead + mSize;
|
||||||
return( mSize );
|
return( mSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Buffer::expand( unsigned int count )
|
unsigned int Buffer::expand( unsigned int count )
|
||||||
{
|
{
|
||||||
int spare = mAllocation - mSize;
|
int spare = mAllocation - mSize;
|
||||||
int headSpace = mHead - mStorage;
|
int headSpace = mHead - mStorage;
|
||||||
int tailSpace = spare - headSpace;
|
int tailSpace = spare - headSpace;
|
||||||
int width = mTail - mHead;
|
int width = mTail - mHead;
|
||||||
if ( spare > (int)count )
|
if ( spare > (int)count )
|
||||||
|
{
|
||||||
|
if ( tailSpace < (int)count )
|
||||||
{
|
{
|
||||||
if ( tailSpace < (int)count )
|
memmove( mStorage, mHead, mSize );
|
||||||
{
|
mHead = mStorage;
|
||||||
memmove( mStorage, mHead, mSize );
|
mTail = mHead + width;
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mAllocation += count;
|
||||||
|
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||||
|
if ( mStorage )
|
||||||
{
|
{
|
||||||
mAllocation += count;
|
memcpy( newStorage, mHead, mSize );
|
||||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
delete[] mStorage;
|
||||||
if ( mStorage )
|
|
||||||
{
|
|
||||||
memcpy( newStorage, mHead, mSize );
|
|
||||||
delete[] mStorage;
|
|
||||||
}
|
|
||||||
mStorage = newStorage;
|
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + width;
|
|
||||||
}
|
}
|
||||||
return( mSize );
|
mStorage = newStorage;
|
||||||
|
mHead = mStorage;
|
||||||
|
mTail = mHead + width;
|
||||||
|
}
|
||||||
|
return( mSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||||
// Make sure there is enough space
|
// Make sure there is enough space
|
||||||
this->expand(bytes);
|
this->expand(bytes);
|
||||||
int bytes_read = read( sd, mTail, bytes );
|
int bytes_read = read( sd, mTail, bytes );
|
||||||
if ( bytes_read > 0 ) {
|
if ( bytes_read > 0 ) {
|
||||||
mTail += bytes_read;
|
mTail += bytes_read;
|
||||||
mSize += bytes_read;
|
mSize += bytes_read;
|
||||||
}
|
}
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
332
src/zm_buffer.h
332
src/zm_buffer.h
|
@ -27,183 +27,183 @@
|
||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
unsigned char *mStorage;
|
unsigned char *mStorage;
|
||||||
unsigned int mAllocation;
|
unsigned int mAllocation;
|
||||||
unsigned int mSize;
|
unsigned int mSize;
|
||||||
unsigned char *mHead;
|
unsigned char *mHead;
|
||||||
unsigned char *mTail;
|
unsigned char *mTail;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mAllocation];
|
||||||
|
mTail = mHead;
|
||||||
|
}
|
||||||
|
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mSize];
|
||||||
|
memcpy( mStorage, pStorage, mSize );
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
}
|
||||||
|
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mSize];
|
||||||
|
memcpy( mStorage, buffer.mHead, mSize );
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
}
|
||||||
|
~Buffer()
|
||||||
|
{
|
||||||
|
delete[] mStorage;
|
||||||
|
}
|
||||||
|
unsigned char *head() const { return( mHead ); }
|
||||||
|
unsigned char *tail() const { return( mTail ); }
|
||||||
|
unsigned int size() const { return( mSize ); }
|
||||||
|
bool empty() const { return( mSize == 0 ); }
|
||||||
|
unsigned int size( unsigned int pSize )
|
||||||
|
{
|
||||||
|
if ( mSize < pSize )
|
||||||
{
|
{
|
||||||
|
expand( pSize-mSize );
|
||||||
}
|
}
|
||||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
return( mSize );
|
||||||
{
|
}
|
||||||
mHead = mStorage = new unsigned char[mAllocation];
|
//unsigned int Allocation() const { return( mAllocation ); }
|
||||||
mTail = mHead;
|
|
||||||
}
|
|
||||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
|
||||||
{
|
|
||||||
mHead = mStorage = new unsigned char[mSize];
|
|
||||||
memcpy( mStorage, pStorage, mSize );
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
|
||||||
{
|
|
||||||
mHead = mStorage = new unsigned char[mSize];
|
|
||||||
memcpy( mStorage, buffer.mHead, mSize );
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
~Buffer()
|
|
||||||
{
|
|
||||||
delete[] mStorage;
|
|
||||||
}
|
|
||||||
unsigned char *head() const { return( mHead ); }
|
|
||||||
unsigned char *tail() const { return( mTail ); }
|
|
||||||
unsigned int size() const { return( mSize ); }
|
|
||||||
bool empty() const { return( mSize == 0 ); }
|
|
||||||
unsigned int size( unsigned int pSize )
|
|
||||||
{
|
|
||||||
if ( mSize < pSize )
|
|
||||||
{
|
|
||||||
expand( pSize-mSize );
|
|
||||||
}
|
|
||||||
return( mSize );
|
|
||||||
}
|
|
||||||
//unsigned int Allocation() const { return( mAllocation ); }
|
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
|
{
|
||||||
|
mSize = 0;
|
||||||
|
mHead = mTail = mStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||||
|
unsigned int assign( const Buffer &buffer )
|
||||||
|
{
|
||||||
|
return( assign( buffer.mHead, buffer.mSize ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim from the front of the buffer
|
||||||
|
unsigned int consume( unsigned int count )
|
||||||
|
{
|
||||||
|
if ( count > mSize )
|
||||||
{
|
{
|
||||||
mSize = 0;
|
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||||
|
count = mSize;
|
||||||
|
}
|
||||||
|
mHead += count;
|
||||||
|
mSize -= count;
|
||||||
|
tidy( 0 );
|
||||||
|
return( count );
|
||||||
|
}
|
||||||
|
// Trim from the end of the buffer
|
||||||
|
unsigned int shrink( unsigned int count )
|
||||||
|
{
|
||||||
|
if ( count > mSize )
|
||||||
|
{
|
||||||
|
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||||
|
count = mSize;
|
||||||
|
}
|
||||||
|
mSize -= count;
|
||||||
|
if ( mTail > (mHead + mSize) )
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
tidy( 0 );
|
||||||
|
return( count );
|
||||||
|
}
|
||||||
|
// Add to the end of the buffer
|
||||||
|
unsigned int expand( unsigned int count );
|
||||||
|
|
||||||
|
// Return pointer to the first pSize bytes and advance the head
|
||||||
|
unsigned char *extract( unsigned int pSize )
|
||||||
|
{
|
||||||
|
if ( pSize > mSize )
|
||||||
|
{
|
||||||
|
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||||
|
pSize = mSize;
|
||||||
|
}
|
||||||
|
unsigned char *oldHead = mHead;
|
||||||
|
mHead += pSize;
|
||||||
|
mSize -= pSize;
|
||||||
|
tidy( 0 );
|
||||||
|
return( oldHead );
|
||||||
|
}
|
||||||
|
// Add bytes to the end of the buffer
|
||||||
|
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||||
|
{
|
||||||
|
expand( pSize );
|
||||||
|
memcpy( mTail, pStorage, pSize );
|
||||||
|
mTail += pSize;
|
||||||
|
mSize += pSize;
|
||||||
|
return( mSize );
|
||||||
|
}
|
||||||
|
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||||
|
{
|
||||||
|
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||||
|
}
|
||||||
|
unsigned int append( const Buffer &buffer )
|
||||||
|
{
|
||||||
|
return( append( buffer.mHead, buffer.mSize ) );
|
||||||
|
}
|
||||||
|
void tidy( bool level=0 )
|
||||||
|
{
|
||||||
|
if ( mHead != mStorage )
|
||||||
|
{
|
||||||
|
if ( mSize == 0 )
|
||||||
mHead = mTail = mStorage;
|
mHead = mTail = mStorage;
|
||||||
}
|
else if ( level )
|
||||||
|
{
|
||||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
if ( (mHead-mStorage) > mSize )
|
||||||
unsigned int assign( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
return( assign( buffer.mHead, buffer.mSize ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim from the front of the buffer
|
|
||||||
unsigned int consume( unsigned int count )
|
|
||||||
{
|
|
||||||
if ( count > mSize )
|
|
||||||
{
|
{
|
||||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
memcpy( mStorage, mHead, mSize );
|
||||||
count = mSize;
|
mHead = mStorage;
|
||||||
|
mTail = mHead + mSize;
|
||||||
}
|
}
|
||||||
mHead += count;
|
}
|
||||||
mSize -= count;
|
|
||||||
tidy( 0 );
|
|
||||||
return( count );
|
|
||||||
}
|
}
|
||||||
// Trim from the end of the buffer
|
}
|
||||||
unsigned int shrink( unsigned int count )
|
|
||||||
{
|
|
||||||
if ( count > mSize )
|
|
||||||
{
|
|
||||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
|
||||||
count = mSize;
|
|
||||||
}
|
|
||||||
mSize -= count;
|
|
||||||
if ( mTail > (mHead + mSize) )
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
tidy( 0 );
|
|
||||||
return( count );
|
|
||||||
}
|
|
||||||
// Add to the end of the buffer
|
|
||||||
unsigned int expand( unsigned int count );
|
|
||||||
|
|
||||||
// Return pointer to the first pSize bytes and advance the head
|
Buffer &operator=( const Buffer &buffer )
|
||||||
unsigned char *extract( unsigned int pSize )
|
{
|
||||||
{
|
assign( buffer );
|
||||||
if ( pSize > mSize )
|
return( *this );
|
||||||
{
|
}
|
||||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
Buffer &operator+=( const Buffer &buffer )
|
||||||
pSize = mSize;
|
{
|
||||||
}
|
append( buffer );
|
||||||
unsigned char *oldHead = mHead;
|
return( *this );
|
||||||
mHead += pSize;
|
}
|
||||||
mSize -= pSize;
|
Buffer &operator+=( unsigned int count )
|
||||||
tidy( 0 );
|
{
|
||||||
return( oldHead );
|
expand( count );
|
||||||
}
|
return( *this );
|
||||||
// Add bytes to the end of the buffer
|
}
|
||||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
Buffer &operator-=( unsigned int count )
|
||||||
{
|
{
|
||||||
expand( pSize );
|
consume( count );
|
||||||
memcpy( mTail, pStorage, pSize );
|
return( *this );
|
||||||
mTail += pSize;
|
}
|
||||||
mSize += pSize;
|
operator unsigned char *() const
|
||||||
return( mSize );
|
{
|
||||||
}
|
return( mHead );
|
||||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
}
|
||||||
{
|
operator char *() const
|
||||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
{
|
||||||
}
|
return( (char *)mHead );
|
||||||
unsigned int append( const Buffer &buffer )
|
}
|
||||||
{
|
unsigned char *operator+(int offset) const
|
||||||
return( append( buffer.mHead, buffer.mSize ) );
|
{
|
||||||
}
|
return( (unsigned char *)(mHead+offset) );
|
||||||
void tidy( bool level=0 )
|
}
|
||||||
{
|
unsigned char operator[](int index) const
|
||||||
if ( mHead != mStorage )
|
{
|
||||||
{
|
return( *(mHead+index) );
|
||||||
if ( mSize == 0 )
|
}
|
||||||
mHead = mTail = mStorage;
|
operator int () const
|
||||||
else if ( level )
|
{
|
||||||
{
|
return( (int)mSize );
|
||||||
if ( (mHead-mStorage) > mSize )
|
}
|
||||||
{
|
int read_into( int sd, unsigned int bytes );
|
||||||
memcpy( mStorage, mHead, mSize );
|
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer &operator=( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
assign( buffer );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator+=( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
append( buffer );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator+=( unsigned int count )
|
|
||||||
{
|
|
||||||
expand( count );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator-=( unsigned int count )
|
|
||||||
{
|
|
||||||
consume( count );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
operator unsigned char *() const
|
|
||||||
{
|
|
||||||
return( mHead );
|
|
||||||
}
|
|
||||||
operator char *() const
|
|
||||||
{
|
|
||||||
return( (char *)mHead );
|
|
||||||
}
|
|
||||||
unsigned char *operator+(int offset) const
|
|
||||||
{
|
|
||||||
return( (unsigned char *)(mHead+offset) );
|
|
||||||
}
|
|
||||||
unsigned char operator[](int index) const
|
|
||||||
{
|
|
||||||
return( *(mHead+index) );
|
|
||||||
}
|
|
||||||
operator int () const
|
|
||||||
{
|
|
||||||
return( (int)mSize );
|
|
||||||
}
|
|
||||||
int read_into( int sd, unsigned int bytes );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_BUFFER_H
|
#endif // ZM_BUFFER_H
|
||||||
|
|
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>
|
#include <string>
|
||||||
|
|
||||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||||
|
|
||||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||||
|
|
||||||
#ifdef HAVE_LIBAVFORMAT
|
#ifdef HAVE_LIBAVFORMAT
|
||||||
#define ZM_HAS_FFMPEG 1
|
#define ZM_HAS_FFMPEG 1
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||||
|
|
||||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||||
|
|
||||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||||
|
|
||||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||||
|
|
||||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||||
|
|
||||||
extern void zmLoadConfig();
|
extern void zmLoadConfig();
|
||||||
|
|
||||||
struct StaticConfig
|
struct StaticConfig
|
||||||
{
|
{
|
||||||
std::string DB_HOST;
|
std::string DB_HOST;
|
||||||
std::string DB_NAME;
|
std::string DB_NAME;
|
||||||
std::string DB_USER;
|
std::string DB_USER;
|
||||||
std::string DB_PASS;
|
std::string DB_PASS;
|
||||||
std::string PATH_WEB;
|
std::string PATH_WEB;
|
||||||
std::string SERVER_NAME;
|
std::string SERVER_NAME;
|
||||||
unsigned int SERVER_ID;
|
unsigned int SERVER_ID;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern StaticConfig staticConfig;
|
extern StaticConfig staticConfig;
|
||||||
|
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
|
||||||
class ConfigItem
|
class ConfigItem
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
char *name;
|
char *name;
|
||||||
char *value;
|
char *value;
|
||||||
char *type;
|
char *type;
|
||||||
|
|
||||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||||
mutable union
|
mutable union
|
||||||
{
|
{
|
||||||
bool boolean_value;
|
bool boolean_value;
|
||||||
int integer_value;
|
int integer_value;
|
||||||
double decimal_value;
|
double decimal_value;
|
||||||
char *string_value;
|
char *string_value;
|
||||||
} cfg_value;
|
} cfg_value;
|
||||||
mutable bool accessed;
|
mutable bool accessed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||||
~ConfigItem();
|
~ConfigItem();
|
||||||
void ConvertValue() const;
|
void ConvertValue() const;
|
||||||
bool BooleanValue() const;
|
bool BooleanValue() const;
|
||||||
int IntegerValue() const;
|
int IntegerValue() const;
|
||||||
double DecimalValue() const;
|
double DecimalValue() const;
|
||||||
const char *StringValue() const;
|
const char *StringValue() const;
|
||||||
|
|
||||||
inline operator bool() const
|
inline operator bool() const
|
||||||
{
|
{
|
||||||
return( BooleanValue() );
|
return( BooleanValue() );
|
||||||
}
|
}
|
||||||
inline operator int() const
|
inline operator int() const
|
||||||
{
|
{
|
||||||
return( IntegerValue() );
|
return( IntegerValue() );
|
||||||
}
|
}
|
||||||
inline operator double() const
|
inline operator double() const
|
||||||
{
|
{
|
||||||
return( DecimalValue() );
|
return( DecimalValue() );
|
||||||
}
|
}
|
||||||
inline operator const char *() const
|
inline operator const char *() const
|
||||||
{
|
{
|
||||||
return( StringValue() );
|
return( StringValue() );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ZM_CFG_DECLARE_LIST
|
ZM_CFG_DECLARE_LIST
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int n_items;
|
int n_items;
|
||||||
ConfigItem **items;
|
ConfigItem **items;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
|
|
||||||
void Load();
|
void Load();
|
||||||
void Assign();
|
void Assign();
|
||||||
const ConfigItem &Item( int id );
|
const ConfigItem &Item( int id );
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Config config;
|
extern Config config;
|
||||||
|
|
|
@ -28,40 +28,40 @@
|
||||||
class Coord
|
class Coord
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Coord() : x(0), y(0)
|
inline Coord() : x(0), y(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline int &X() { return( x ); }
|
inline int &X() { return( x ); }
|
||||||
inline const int &X() const { return( x ); }
|
inline const int &X() const { return( x ); }
|
||||||
inline int &Y() { return( y ); }
|
inline int &Y() { return( y ); }
|
||||||
inline const int &Y() const { return( y ); }
|
inline const int &Y() const { return( y ); }
|
||||||
|
|
||||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||||
{
|
{
|
||||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||||
|
|
||||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_COORD_H
|
#endif // ZM_COORD_H
|
||||||
|
|
|
@ -27,42 +27,42 @@
|
||||||
class Exception
|
class Exception
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string mMessage;
|
std::string mMessage;
|
||||||
Severity mSeverity;
|
Severity mSeverity;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string &getMessage() const
|
const std::string &getMessage() const
|
||||||
{
|
{
|
||||||
return( mMessage );
|
return( mMessage );
|
||||||
}
|
}
|
||||||
Severity getSeverity() const
|
Severity getSeverity() const
|
||||||
{
|
{
|
||||||
return( mSeverity );
|
return( mSeverity );
|
||||||
}
|
}
|
||||||
bool isInfo() const
|
bool isInfo() const
|
||||||
{
|
{
|
||||||
return( mSeverity == INFO );
|
return( mSeverity == INFO );
|
||||||
}
|
}
|
||||||
bool isWarning() const
|
bool isWarning() const
|
||||||
{
|
{
|
||||||
return( mSeverity == WARNING );
|
return( mSeverity == WARNING );
|
||||||
}
|
}
|
||||||
bool isError() const
|
bool isError() const
|
||||||
{
|
{
|
||||||
return( mSeverity == ERROR );
|
return( mSeverity == ERROR );
|
||||||
}
|
}
|
||||||
bool isFatal() const
|
bool isFatal() const
|
||||||
{
|
{
|
||||||
return( mSeverity == FATAL );
|
return( mSeverity == FATAL );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EXCEPTION_H
|
#endif // ZM_EXCEPTION_H
|
||||||
|
|
6660
src/zm_font.h
6660
src/zm_font.h
File diff suppressed because it is too large
Load Diff
9474
src/zm_image.cpp
9474
src/zm_image.cpp
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||||
{
|
{
|
||||||
m_Detectors = source.m_Detectors;
|
m_Detectors = source.m_Detectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||||
*/
|
*/
|
||||||
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
||||||
{
|
{
|
||||||
m_Detectors = source.m_Detectors;
|
m_Detectors = source.m_Detectors;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ImageAnalyser::~ImageAnalyser()
|
ImageAnalyser::~ImageAnalyser()
|
||||||
{
|
{
|
||||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||||
It != m_Detectors.rend();
|
It != m_Detectors.rend();
|
||||||
++It)
|
++It)
|
||||||
delete *It;
|
delete *It;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser()
|
||||||
*/
|
*/
|
||||||
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
||||||
{
|
{
|
||||||
Event::StringSet zoneSet;
|
Event::StringSet zoneSet;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
|
||||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||||
It != m_Detectors.end();
|
It != m_Detectors.end();
|
||||||
++It)
|
++It)
|
||||||
|
{
|
||||||
|
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||||
|
if (detect_score)
|
||||||
{
|
{
|
||||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
score += detect_score;
|
||||||
if (detect_score)
|
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||||
{
|
if (det_cause.length())
|
||||||
score += detect_score;
|
det_cause += ", ";
|
||||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
det_cause += (*It)->getDetectionCause();
|
||||||
if (det_cause.length())
|
|
||||||
det_cause += ", ";
|
|
||||||
det_cause += (*It)->getDetectionCause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return score;
|
}
|
||||||
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,18 +22,18 @@ using namespace std;
|
||||||
//! Class for handling image detection.
|
//! Class for handling image detection.
|
||||||
class ImageAnalyser {
|
class ImageAnalyser {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//!Default constructor.
|
//!Default constructor.
|
||||||
ImageAnalyser() {};
|
ImageAnalyser() {};
|
||||||
|
|
||||||
//! Destructor.
|
//! Destructor.
|
||||||
~ImageAnalyser();
|
~ImageAnalyser();
|
||||||
|
|
||||||
//! Copy constructor.
|
//! Copy constructor.
|
||||||
ImageAnalyser(const ImageAnalyser& source);
|
ImageAnalyser(const ImageAnalyser& source);
|
||||||
|
|
||||||
//! Overloaded operator=.
|
//! Overloaded operator=.
|
||||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
456
src/zm_jpeg.cpp
456
src/zm_jpeg.cpp
|
@ -32,65 +32,65 @@ static int jpeg_err_count = 0;
|
||||||
|
|
||||||
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
||||||
{
|
{
|
||||||
static char buffer[JMSG_LENGTH_MAX];
|
static char buffer[JMSG_LENGTH_MAX];
|
||||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||||
|
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
|
|
||||||
Error( "%s", buffer );
|
Error( "%s", buffer );
|
||||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||||
{
|
{
|
||||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||||
}
|
}
|
||||||
|
|
||||||
longjmp( zmerr->setjmp_buffer, 1 );
|
longjmp( zmerr->setjmp_buffer, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
||||||
{
|
{
|
||||||
static char buffer[JMSG_LENGTH_MAX];
|
static char buffer[JMSG_LENGTH_MAX];
|
||||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||||
|
|
||||||
if ( msg_level < 0 )
|
if ( msg_level < 0 )
|
||||||
{
|
{
|
||||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||||
* the policy implemented here is to show only the first warning,
|
* the policy implemented here is to show only the first warning,
|
||||||
* unless trace_level >= 3.
|
* unless trace_level >= 3.
|
||||||
*/
|
*/
|
||||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||||
{
|
{
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||||
Warning( "%s", buffer );
|
Warning( "%s", buffer );
|
||||||
}
|
}
|
||||||
/* Always count warnings in num_warnings. */
|
/* Always count warnings in num_warnings. */
|
||||||
zmerr->pub.num_warnings++;
|
zmerr->pub.num_warnings++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||||
if ( zmerr->pub.trace_level >= msg_level )
|
if ( zmerr->pub.trace_level >= msg_level )
|
||||||
{
|
{
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
Debug( msg_level, "%s", buffer );
|
Debug( msg_level, "%s", buffer );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expanded data destination object for memory */
|
/* Expanded data destination object for memory */
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
struct jpeg_destination_mgr pub; /* public fields */
|
struct jpeg_destination_mgr pub; /* public fields */
|
||||||
|
|
||||||
JOCTET *outbuffer; /* target buffer */
|
JOCTET *outbuffer; /* target buffer */
|
||||||
int *outbuffer_size;
|
int *outbuffer_size;
|
||||||
JOCTET *buffer; /* start of buffer */
|
JOCTET *buffer; /* start of buffer */
|
||||||
} mem_destination_mgr;
|
} mem_destination_mgr;
|
||||||
|
|
||||||
typedef mem_destination_mgr * mem_dest_ptr;
|
typedef mem_destination_mgr * mem_dest_ptr;
|
||||||
|
|
||||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize destination --- called by jpeg_start_compress
|
* Initialize destination --- called by jpeg_start_compress
|
||||||
|
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
|
||||||
|
|
||||||
static void init_destination (j_compress_ptr cinfo)
|
static void init_destination (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
|
|
||||||
/* Allocate the output buffer --- it will be released when done with image */
|
/* Allocate the output buffer --- it will be released when done with image */
|
||||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||||
|
|
||||||
dest->pub.next_output_byte = dest->buffer;
|
dest->pub.next_output_byte = dest->buffer;
|
||||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
*(dest->outbuffer_size) = 0;
|
*(dest->outbuffer_size) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo)
|
||||||
|
|
||||||
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
|
|
||||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
dest->pub.next_output_byte = dest->buffer;
|
dest->pub.next_output_byte = dest->buffer;
|
||||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
return( TRUE );
|
return( TRUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||||
|
|
||||||
static void term_destination (j_compress_ptr cinfo)
|
static void term_destination (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||||
|
|
||||||
if ( datacount > 0 )
|
if ( datacount > 0 )
|
||||||
{
|
{
|
||||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||||
*(dest->outbuffer_size) += datacount;
|
*(dest->outbuffer_size) += datacount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo)
|
||||||
|
|
||||||
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest;
|
mem_dest_ptr dest;
|
||||||
|
|
||||||
/* The destination object is made permanent so that multiple JPEG images
|
/* The destination object is made permanent so that multiple JPEG images
|
||||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||||
* This makes it dangerous to use this manager and a different destination
|
* This makes it dangerous to use this manager and a different destination
|
||||||
* manager serially with the same JPEG object, because their private object
|
* manager serially with the same JPEG object, because their private object
|
||||||
* sizes may be different. Caveat programmer.
|
* sizes may be different. Caveat programmer.
|
||||||
*/
|
*/
|
||||||
if ( cinfo->dest == NULL )
|
if ( cinfo->dest == NULL )
|
||||||
{
|
{
|
||||||
/* first time for this JPEG object? */
|
/* first time for this JPEG object? */
|
||||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||||
}
|
}
|
||||||
|
|
||||||
dest = (mem_dest_ptr) cinfo->dest;
|
dest = (mem_dest_ptr) cinfo->dest;
|
||||||
dest->pub.init_destination = init_destination;
|
dest->pub.init_destination = init_destination;
|
||||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||||
dest->pub.term_destination = term_destination;
|
dest->pub.term_destination = term_destination;
|
||||||
dest->outbuffer = outbuffer;
|
dest->outbuffer = outbuffer;
|
||||||
dest->outbuffer_size = outbuffer_size;
|
dest->outbuffer_size = outbuffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expanded data source object for memory input */
|
/* Expanded data source object for memory input */
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
struct jpeg_source_mgr pub; /* public fields */
|
struct jpeg_source_mgr pub; /* public fields */
|
||||||
|
|
||||||
JOCTET * inbuffer; /* source stream */
|
JOCTET * inbuffer; /* source stream */
|
||||||
int inbuffer_size;
|
int inbuffer_size;
|
||||||
int inbuffer_size_hwm; /* High water mark */
|
int inbuffer_size_hwm; /* High water mark */
|
||||||
|
|
||||||
JOCTET * buffer; /* start of buffer */
|
JOCTET * buffer; /* start of buffer */
|
||||||
boolean start_of_data; /* have we gotten any data yet? */
|
boolean start_of_data; /* have we gotten any data yet? */
|
||||||
} mem_source_mgr;
|
} mem_source_mgr;
|
||||||
|
|
||||||
typedef mem_source_mgr * mem_src_ptr;
|
typedef mem_source_mgr * mem_src_ptr;
|
||||||
|
|
||||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize source --- called by jpeg_read_header
|
* Initialize source --- called by jpeg_read_header
|
||||||
|
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
|
||||||
|
|
||||||
static void init_source (j_decompress_ptr cinfo)
|
static void init_source (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
|
|
||||||
/* We reset the empty-input-file flag for each image,
|
/* We reset the empty-input-file flag for each image,
|
||||||
* but we don't clear the input buffer.
|
* but we don't clear the input buffer.
|
||||||
* This is correct behavior for reading a series of images from one source.
|
* This is correct behavior for reading a series of images from one source.
|
||||||
*/
|
*/
|
||||||
src->start_of_data = TRUE;
|
src->start_of_data = TRUE;
|
||||||
src->pub.bytes_in_buffer = 0;
|
src->pub.bytes_in_buffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo)
|
||||||
|
|
||||||
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
size_t nbytes;
|
size_t nbytes;
|
||||||
|
|
||||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||||
nbytes = src->inbuffer_size;
|
nbytes = src->inbuffer_size;
|
||||||
|
|
||||||
if ( nbytes <= 0 )
|
if ( nbytes <= 0 )
|
||||||
{
|
{
|
||||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||||
/* Insert a fake EOI marker */
|
/* Insert a fake EOI marker */
|
||||||
src->buffer[0] = (JOCTET) 0xFF;
|
src->buffer[0] = (JOCTET) 0xFF;
|
||||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||||
nbytes = 2;
|
nbytes = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
src->pub.next_input_byte = src->buffer;
|
src->pub.next_input_byte = src->buffer;
|
||||||
src->pub.bytes_in_buffer = nbytes;
|
src->pub.bytes_in_buffer = nbytes;
|
||||||
src->start_of_data = FALSE;
|
src->start_of_data = FALSE;
|
||||||
|
|
||||||
return( TRUE );
|
return( TRUE );
|
||||||
}
|
}
|
||||||
|
@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||||
|
|
||||||
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
|
|
||||||
/* Just a dumb implementation for now. Could use fseek() except
|
/* Just a dumb implementation for now. Could use fseek() except
|
||||||
* it doesn't work on pipes. Not clear that being smart is worth
|
* it doesn't work on pipes. Not clear that being smart is worth
|
||||||
* any trouble anyway --- large skips are infrequent.
|
* any trouble anyway --- large skips are infrequent.
|
||||||
*/
|
*/
|
||||||
if ( num_bytes > 0 )
|
if ( num_bytes > 0 )
|
||||||
|
{
|
||||||
|
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||||
{
|
{
|
||||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||||
{
|
(void) fill_input_buffer(cinfo);
|
||||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
/* note we assume that fill_input_buffer will never return FALSE,
|
||||||
(void) fill_input_buffer(cinfo);
|
* so suspension need not be handled.
|
||||||
/* note we assume that fill_input_buffer will never return FALSE,
|
*/
|
||||||
* so suspension need not be handled.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
src->pub.next_input_byte += (size_t) num_bytes;
|
|
||||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
|
||||||
}
|
}
|
||||||
|
src->pub.next_input_byte += (size_t) num_bytes;
|
||||||
|
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||||
|
|
||||||
static void term_source (j_decompress_ptr cinfo)
|
static void term_source (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
/* no work necessary here */
|
/* no work necessary here */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -354,114 +354,114 @@ static void term_source (j_decompress_ptr cinfo)
|
||||||
|
|
||||||
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||||
{
|
{
|
||||||
mem_src_ptr src;
|
mem_src_ptr src;
|
||||||
|
|
||||||
/* The source object and input buffer are made permanent so that a series
|
|
||||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
|
||||||
* only before the first one. (If we discarded the buffer at the end of
|
|
||||||
* one image, we'd likely lose the start of the next one.)
|
|
||||||
* This makes it unsafe to use this manager and a different source
|
|
||||||
* manager serially with the same JPEG object. Caveat programmer.
|
|
||||||
*/
|
|
||||||
if ( cinfo->src == NULL )
|
|
||||||
{
|
|
||||||
/* first time for this JPEG object? */
|
|
||||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
|
||||||
src = (mem_src_ptr) cinfo->src;
|
|
||||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
|
||||||
src->inbuffer_size_hwm = inbuffer_size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src = (mem_src_ptr) cinfo->src;
|
|
||||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
|
||||||
{
|
|
||||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
|
||||||
src->inbuffer_size_hwm = inbuffer_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* The source object and input buffer are made permanent so that a series
|
||||||
|
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||||
|
* only before the first one. (If we discarded the buffer at the end of
|
||||||
|
* one image, we'd likely lose the start of the next one.)
|
||||||
|
* This makes it unsafe to use this manager and a different source
|
||||||
|
* manager serially with the same JPEG object. Caveat programmer.
|
||||||
|
*/
|
||||||
|
if ( cinfo->src == NULL )
|
||||||
|
{
|
||||||
|
/* first time for this JPEG object? */
|
||||||
|
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||||
src = (mem_src_ptr) cinfo->src;
|
src = (mem_src_ptr) cinfo->src;
|
||||||
src->pub.init_source = init_source;
|
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||||
src->pub.fill_input_buffer = fill_input_buffer;
|
src->inbuffer_size_hwm = inbuffer_size;
|
||||||
src->pub.skip_input_data = skip_input_data;
|
}
|
||||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
else
|
||||||
src->pub.term_source = term_source;
|
{
|
||||||
src->inbuffer = (JOCTET *)inbuffer;
|
src = (mem_src_ptr) cinfo->src;
|
||||||
src->inbuffer_size = inbuffer_size;
|
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
{
|
||||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||||
|
src->inbuffer_size_hwm = inbuffer_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src = (mem_src_ptr) cinfo->src;
|
||||||
|
src->pub.init_source = init_source;
|
||||||
|
src->pub.fill_input_buffer = fill_input_buffer;
|
||||||
|
src->pub.skip_input_data = skip_input_data;
|
||||||
|
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||||
|
src->pub.term_source = term_source;
|
||||||
|
src->inbuffer = (JOCTET *)inbuffer;
|
||||||
|
src->inbuffer_size = inbuffer_size;
|
||||||
|
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||||
|
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||||
}
|
}
|
||||||
|
|
||||||
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
||||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||||
static const JHUFF_TBL dclumin = {
|
static const JHUFF_TBL dclumin = {
|
||||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL dcchrome = {
|
static const JHUFF_TBL dcchrome = {
|
||||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL aclumin = {
|
static const JHUFF_TBL aclumin = {
|
||||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||||
0xf9, 0xfa },
|
0xf9, 0xfa },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL acchrome = {
|
static const JHUFF_TBL acchrome = {
|
||||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||||
0xf9, 0xfa },
|
0xf9, 0xfa },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
|
|
||||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ extern "C"
|
||||||
/* Stuff for overriden error handlers */
|
/* Stuff for overriden error handlers */
|
||||||
struct zm_error_mgr
|
struct zm_error_mgr
|
||||||
{
|
{
|
||||||
struct jpeg_error_mgr pub;
|
struct jpeg_error_mgr pub;
|
||||||
jmp_buf setjmp_buffer;
|
jmp_buf setjmp_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct zm_error_mgr *zm_error_ptr;
|
typedef struct zm_error_mgr *zm_error_ptr;
|
||||||
|
|
302
src/zm_logger.h
302
src/zm_logger.h
|
@ -33,190 +33,190 @@
|
||||||
class Logger
|
class Logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
NOOPT=-6,
|
NOOPT=-6,
|
||||||
NOLOG,
|
NOLOG,
|
||||||
PANIC,
|
PANIC,
|
||||||
FATAL,
|
FATAL,
|
||||||
ERROR,
|
ERROR,
|
||||||
WARNING,
|
WARNING,
|
||||||
INFO,
|
INFO,
|
||||||
DEBUG1,
|
DEBUG1,
|
||||||
DEBUG2,
|
DEBUG2,
|
||||||
DEBUG3,
|
DEBUG3,
|
||||||
DEBUG4,
|
DEBUG4,
|
||||||
DEBUG5,
|
DEBUG5,
|
||||||
DEBUG6,
|
DEBUG6,
|
||||||
DEBUG7,
|
DEBUG7,
|
||||||
DEBUG8,
|
DEBUG8,
|
||||||
DEBUG9
|
DEBUG9
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int Level;
|
typedef int Level;
|
||||||
|
|
||||||
typedef std::map<Level,std::string> StringMap;
|
typedef std::map<Level,std::string> StringMap;
|
||||||
typedef std::map<Level,int> IntMap;
|
typedef std::map<Level,int> IntMap;
|
||||||
|
|
||||||
class Options
|
class Options
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int mTermLevel;
|
int mTermLevel;
|
||||||
int mDatabaseLevel;
|
int mDatabaseLevel;
|
||||||
int mFileLevel;
|
int mFileLevel;
|
||||||
int mSyslogLevel;
|
int mSyslogLevel;
|
||||||
|
|
||||||
std::string mLogPath;
|
|
||||||
std::string mLogFile;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
|
||||||
mTermLevel( termLevel ),
|
|
||||||
mDatabaseLevel( databaseLevel ),
|
|
||||||
mFileLevel( fileLevel ),
|
|
||||||
mSyslogLevel( syslogLevel ),
|
|
||||||
mLogPath( logPath ),
|
|
||||||
mLogFile( logFile )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool smInitialised;
|
|
||||||
static Logger *smInstance;
|
|
||||||
|
|
||||||
static StringMap smCodes;
|
|
||||||
static IntMap smSyslogPriorities;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool mInitialised;
|
|
||||||
|
|
||||||
std::string mId;
|
|
||||||
std::string mIdRoot;
|
|
||||||
std::string mIdArgs;
|
|
||||||
|
|
||||||
Level mLevel; // Level that is currently in operation
|
|
||||||
Level mTermLevel; // Maximum level output via terminal
|
|
||||||
Level mDatabaseLevel; // Maximum level output via database
|
|
||||||
Level mFileLevel; // Maximum level output via file
|
|
||||||
Level mSyslogLevel; // Maximum level output via syslog
|
|
||||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
|
||||||
|
|
||||||
bool mDbConnected;
|
|
||||||
MYSQL mDbConnection;
|
|
||||||
std::string mLogPath;
|
std::string mLogPath;
|
||||||
std::string mLogFile;
|
std::string mLogFile;
|
||||||
FILE *mLogFileFP;
|
|
||||||
|
|
||||||
bool mHasTerm;
|
public:
|
||||||
bool mFlush;
|
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||||
|
mTermLevel( termLevel ),
|
||||||
|
mDatabaseLevel( databaseLevel ),
|
||||||
|
mFileLevel( fileLevel ),
|
||||||
|
mSyslogLevel( syslogLevel ),
|
||||||
|
mLogPath( logPath ),
|
||||||
|
mLogFile( logFile )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void usrHandler( int sig );
|
static bool smInitialised;
|
||||||
|
static Logger *smInstance;
|
||||||
|
|
||||||
public:
|
static StringMap smCodes;
|
||||||
friend void logInit( const char *name, const Options &options );
|
static IntMap smSyslogPriorities;
|
||||||
friend void logTerm();
|
|
||||||
|
|
||||||
static Logger *fetch()
|
|
||||||
{
|
|
||||||
if ( !smInstance )
|
|
||||||
{
|
|
||||||
smInstance = new Logger();
|
|
||||||
Options options;
|
|
||||||
smInstance->initialise( "undef", options );
|
|
||||||
}
|
|
||||||
return( smInstance );
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger();
|
bool mInitialised;
|
||||||
~Logger();
|
|
||||||
|
|
||||||
public:
|
std::string mId;
|
||||||
void initialise( const std::string &id, const Options &options );
|
std::string mIdRoot;
|
||||||
void terminate();
|
std::string mIdArgs;
|
||||||
|
|
||||||
|
Level mLevel; // Level that is currently in operation
|
||||||
|
Level mTermLevel; // Maximum level output via terminal
|
||||||
|
Level mDatabaseLevel; // Maximum level output via database
|
||||||
|
Level mFileLevel; // Maximum level output via file
|
||||||
|
Level mSyslogLevel; // Maximum level output via syslog
|
||||||
|
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||||
|
|
||||||
|
bool mDbConnected;
|
||||||
|
MYSQL mDbConnection;
|
||||||
|
std::string mLogPath;
|
||||||
|
std::string mLogFile;
|
||||||
|
FILE *mLogFileFP;
|
||||||
|
|
||||||
|
bool mHasTerm;
|
||||||
|
bool mFlush;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int limit( int level )
|
static void usrHandler( int sig );
|
||||||
{
|
|
||||||
if ( level > DEBUG9 )
|
|
||||||
return( DEBUG9 );
|
|
||||||
if ( level < NOLOG )
|
|
||||||
return( NOLOG );
|
|
||||||
return( level );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
|
||||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
|
||||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
|
||||||
char *getTargettedEnv( const std::string &name );
|
|
||||||
|
|
||||||
void loadEnv();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string &id() const
|
friend void logInit( const char *name, const Options &options );
|
||||||
|
friend void logTerm();
|
||||||
|
|
||||||
|
static Logger *fetch()
|
||||||
|
{
|
||||||
|
if ( !smInstance )
|
||||||
{
|
{
|
||||||
return( mId );
|
smInstance = new Logger();
|
||||||
|
Options options;
|
||||||
|
smInstance->initialise( "undef", options );
|
||||||
}
|
}
|
||||||
|
return( smInstance );
|
||||||
const std::string &id( const std::string &id );
|
}
|
||||||
|
|
||||||
Level level() const
|
|
||||||
{
|
|
||||||
return( mLevel );
|
|
||||||
}
|
|
||||||
Level level( Level=NOOPT );
|
|
||||||
|
|
||||||
bool debugOn()
|
|
||||||
{
|
|
||||||
return( mEffectiveLevel >= DEBUG1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
Level termLevel( Level=NOOPT );
|
|
||||||
Level databaseLevel( Level=NOOPT );
|
|
||||||
Level fileLevel( Level=NOOPT );
|
|
||||||
Level syslogLevel( Level=NOOPT );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void logFile( const std::string &logFile );
|
Logger();
|
||||||
void openFile();
|
~Logger();
|
||||||
void closeFile();
|
|
||||||
void openSyslog();
|
|
||||||
void closeSyslog();
|
|
||||||
void closeDatabase();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
void initialise( const std::string &id, const Options &options );
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int limit( int level )
|
||||||
|
{
|
||||||
|
if ( level > DEBUG9 )
|
||||||
|
return( DEBUG9 );
|
||||||
|
if ( level < NOLOG )
|
||||||
|
return( NOLOG );
|
||||||
|
return( level );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||||
|
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||||
|
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||||
|
char *getTargettedEnv( const std::string &name );
|
||||||
|
|
||||||
|
void loadEnv();
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string &id() const
|
||||||
|
{
|
||||||
|
return( mId );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &id( const std::string &id );
|
||||||
|
|
||||||
|
Level level() const
|
||||||
|
{
|
||||||
|
return( mLevel );
|
||||||
|
}
|
||||||
|
Level level( Level=NOOPT );
|
||||||
|
|
||||||
|
bool debugOn()
|
||||||
|
{
|
||||||
|
return( mEffectiveLevel >= DEBUG1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Level termLevel( Level=NOOPT );
|
||||||
|
Level databaseLevel( Level=NOOPT );
|
||||||
|
Level fileLevel( Level=NOOPT );
|
||||||
|
Level syslogLevel( Level=NOOPT );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void logFile( const std::string &logFile );
|
||||||
|
void openFile();
|
||||||
|
void closeFile();
|
||||||
|
void openSyslog();
|
||||||
|
void closeSyslog();
|
||||||
|
void closeDatabase();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||||
};
|
};
|
||||||
|
|
||||||
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
||||||
void logTerm();
|
void logTerm();
|
||||||
inline const std::string &logId()
|
inline const std::string &logId()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->id() );
|
return( Logger::fetch()->id() );
|
||||||
}
|
}
|
||||||
inline Logger::Level logLevel()
|
inline Logger::Level logLevel()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->level() );
|
return( Logger::fetch()->level() );
|
||||||
}
|
}
|
||||||
inline void logCapLevel( Logger::Level level )
|
inline void logCapLevel( Logger::Level level )
|
||||||
{
|
{
|
||||||
Logger::fetch()->level( level );
|
Logger::fetch()->level( level );
|
||||||
}
|
}
|
||||||
inline Logger::Level logDebugging()
|
inline Logger::Level logDebugging()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->debugOn() );
|
return( Logger::fetch()->debugOn() );
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logPrintf(logLevel,params...) {\
|
#define logPrintf(logLevel,params...) {\
|
||||||
if ( logLevel <= Logger::fetch()->level() )\
|
if ( logLevel <= Logger::fetch()->level() )\
|
||||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logHexdump(logLevel,data,len) {\
|
#define logHexdump(logLevel,data,len) {\
|
||||||
if ( logLevel <= Logger::fetch()->level() )\
|
if ( logLevel <= Logger::fetch()->level() )\
|
||||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug compiled out */
|
/* Debug compiled out */
|
||||||
#ifndef DBG_OFF
|
#ifndef DBG_OFF
|
||||||
|
@ -228,19 +228,19 @@ inline Logger::Level logDebugging()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Standard debug calls */
|
/* Standard debug calls */
|
||||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||||
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
||||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||||
#define Log() Info("Log")
|
#define Log() Info("Log")
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||||
#else
|
#else
|
||||||
#define Enter(level)
|
#define Enter(level)
|
||||||
#define Exit(level)
|
#define Exit(level)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ZM_LOGGER_H
|
#endif // ZM_LOGGER_H
|
||||||
|
|
|
@ -24,138 +24,138 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
|
|
||||||
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
||||||
uint8_t* retptr;
|
uint8_t* retptr;
|
||||||
#if HAVE_POSIX_MEMALIGN
|
#if HAVE_POSIX_MEMALIGN
|
||||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return retptr;
|
return retptr;
|
||||||
#else
|
#else
|
||||||
uint8_t* alloc;
|
uint8_t* alloc;
|
||||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||||
|
|
||||||
if(retptr == NULL)
|
if(retptr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
alloc = retptr + sizeof(void*);
|
alloc = retptr + sizeof(void*);
|
||||||
|
|
||||||
if(((long)alloc % reqalignment) != 0)
|
if(((long)alloc % reqalignment) != 0)
|
||||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||||
|
|
||||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||||
|
|
||||||
return alloc;
|
return alloc;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void zm_freealigned(void* ptr) {
|
inline void zm_freealigned(void* ptr) {
|
||||||
#if HAVE_POSIX_MEMALIGN
|
#if HAVE_POSIX_MEMALIGN
|
||||||
free(ptr);
|
free(ptr);
|
||||||
#else
|
#else
|
||||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !accept || !*accept )
|
if ( limit <= 0 || !s || !accept || !*accept )
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
register unsigned int i,j;
|
|
||||||
size_t acc_len = strlen( accept );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
|
||||||
{
|
|
||||||
for ( j = 0; j < acc_len; j++ )
|
|
||||||
{
|
|
||||||
if ( *s == accept[j] )
|
|
||||||
{
|
|
||||||
return( (char *)s );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
|
register unsigned int i,j;
|
||||||
|
size_t acc_len = strlen( accept );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
for ( j = 0; j < acc_len; j++ )
|
||||||
|
{
|
||||||
|
if ( *s == accept[j] )
|
||||||
|
{
|
||||||
|
return( (char *)s );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char *memstr( register const char *s, const char *n, size_t limit )
|
inline char *memstr( register const char *s, const char *n, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !n )
|
if ( limit <= 0 || !s || !n )
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
if ( !*n )
|
|
||||||
return( (char *)s );
|
|
||||||
|
|
||||||
register unsigned int i,j,k;
|
|
||||||
size_t n_len = strlen( n );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; i++, s++ )
|
|
||||||
{
|
|
||||||
if ( *s != *n )
|
|
||||||
continue;
|
|
||||||
j = 1;
|
|
||||||
k = 1;
|
|
||||||
while ( true )
|
|
||||||
{
|
|
||||||
if ( k >= n_len )
|
|
||||||
return( (char *)s );
|
|
||||||
if ( s[j++] != n[k++] )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
|
if ( !*n )
|
||||||
|
return( (char *)s );
|
||||||
|
|
||||||
|
register unsigned int i,j,k;
|
||||||
|
size_t n_len = strlen( n );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; i++, s++ )
|
||||||
|
{
|
||||||
|
if ( *s != *n )
|
||||||
|
continue;
|
||||||
|
j = 1;
|
||||||
|
k = 1;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
if ( k >= n_len )
|
||||||
|
return( (char *)s );
|
||||||
|
if ( s[j++] != n[k++] )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !accept || !*accept )
|
if ( limit <= 0 || !s || !accept || !*accept )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
register unsigned int i,j;
|
register unsigned int i,j;
|
||||||
size_t acc_len = strlen( accept );
|
size_t acc_len = strlen( accept );
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
register bool found = false;
|
||||||
|
for ( j = 0; j < acc_len; j++ )
|
||||||
{
|
{
|
||||||
register bool found = false;
|
if ( *s == accept[j] )
|
||||||
for ( j = 0; j < acc_len; j++ )
|
{
|
||||||
{
|
found = true;
|
||||||
if ( *s == accept[j] )
|
break;
|
||||||
{
|
}
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !found )
|
|
||||||
{
|
|
||||||
return( i );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return( limit );
|
if ( !found )
|
||||||
|
{
|
||||||
|
return( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( limit );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !reject )
|
if ( limit <= 0 || !s || !reject )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
if ( !*reject )
|
if ( !*reject )
|
||||||
return( limit );
|
|
||||||
|
|
||||||
register unsigned int i,j;
|
|
||||||
size_t rej_len = strlen( reject );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
|
||||||
{
|
|
||||||
for ( j = 0; j < rej_len; j++ )
|
|
||||||
{
|
|
||||||
if ( *s == reject[j] )
|
|
||||||
{
|
|
||||||
return( i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( limit );
|
return( limit );
|
||||||
|
|
||||||
|
register unsigned int i,j;
|
||||||
|
size_t rej_len = strlen( reject );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
for ( j = 0; j < rej_len; j++ )
|
||||||
|
{
|
||||||
|
if ( *s == reject[j] )
|
||||||
|
{
|
||||||
|
return( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( limit );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_MEM_UTILS_H
|
#endif // ZM_MEM_UTILS_H
|
||||||
|
|
|
@ -27,60 +27,60 @@
|
||||||
class VideoStream
|
class VideoStream
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct MimeData
|
struct MimeData
|
||||||
{
|
{
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *mime_type;
|
const char *mime_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool initialised;
|
static bool initialised;
|
||||||
static struct MimeData mime_data[];
|
static struct MimeData mime_data[];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char *codec_and_format;
|
char *codec_and_format;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *codec_name;
|
const char *codec_name;
|
||||||
enum _AVPIXELFORMAT pf;
|
enum _AVPIXELFORMAT pf;
|
||||||
AVOutputFormat *of;
|
AVOutputFormat *of;
|
||||||
AVFormatContext *ofc;
|
AVFormatContext *ofc;
|
||||||
AVStream *ost;
|
AVStream *ost;
|
||||||
AVCodec *codec;
|
AVCodec *codec;
|
||||||
AVFrame *opicture;
|
AVFrame *opicture;
|
||||||
AVFrame *tmp_opicture;
|
AVFrame *tmp_opicture;
|
||||||
uint8_t *video_outbuf;
|
uint8_t *video_outbuf;
|
||||||
int video_outbuf_size;
|
int video_outbuf_size;
|
||||||
double last_pts;
|
double last_pts;
|
||||||
|
|
||||||
pthread_t streaming_thread;
|
pthread_t streaming_thread;
|
||||||
bool do_streaming;
|
bool do_streaming;
|
||||||
uint8_t *buffer_copy;
|
uint8_t *buffer_copy;
|
||||||
bool add_timestamp;
|
bool add_timestamp;
|
||||||
unsigned int timestamp;
|
unsigned int timestamp;
|
||||||
pthread_mutex_t *buffer_copy_lock;
|
pthread_mutex_t *buffer_copy_lock;
|
||||||
int buffer_copy_size;
|
int buffer_copy_size;
|
||||||
int buffer_copy_used;
|
int buffer_copy_used;
|
||||||
AVPacket** packet_buffers;
|
AVPacket** packet_buffers;
|
||||||
int packet_index;
|
int packet_index;
|
||||||
int SendPacket(AVPacket *packet);
|
int SendPacket(AVPacket *packet);
|
||||||
static void* StreamingThreadCallback(void *ctx);
|
static void* StreamingThreadCallback(void *ctx);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
|
|
||||||
void SetupFormat( );
|
void SetupFormat( );
|
||||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||||
void SetParameters();
|
void SetParameters();
|
||||||
void ActuallyOpenStream();
|
void ActuallyOpenStream();
|
||||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||||
~VideoStream();
|
~VideoStream();
|
||||||
const char *MimeType() const;
|
const char *MimeType() const;
|
||||||
void OpenStream();
|
void OpenStream();
|
||||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
140
src/zm_poly.cpp
140
src/zm_poly.cpp
|
@ -28,95 +28,95 @@
|
||||||
|
|
||||||
void Polygon::calcArea()
|
void Polygon::calcArea()
|
||||||
{
|
{
|
||||||
double float_area = 0.0L;
|
double float_area = 0.0L;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||||
float_area += trap_area;
|
float_area += trap_area;
|
||||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||||
}
|
}
|
||||||
area = (int)round(fabs(float_area));
|
area = (int)round(fabs(float_area));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Polygon::calcCentre()
|
void Polygon::calcCentre()
|
||||||
{
|
{
|
||||||
if ( !area && n_coords )
|
if ( !area && n_coords )
|
||||||
calcArea();
|
calcArea();
|
||||||
double float_x = 0.0L, float_y = 0.0L;
|
double float_x = 0.0L, float_y = 0.0L;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||||
}
|
}
|
||||||
float_x /= (6*area);
|
float_x /= (6*area);
|
||||||
float_y /= (6*area);
|
float_y /= (6*area);
|
||||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
||||||
{
|
{
|
||||||
coords = new Coord[n_coords];
|
coords = new Coord[n_coords];
|
||||||
|
|
||||||
int min_x = -1;
|
int min_x = -1;
|
||||||
int max_x = -1;
|
int max_x = -1;
|
||||||
int min_y = -1;
|
int min_y = -1;
|
||||||
int max_y = -1;
|
int max_y = -1;
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_coords[i];
|
coords[i] = p_coords[i];
|
||||||
if ( min_x == -1 || coords[i].X() < min_x )
|
if ( min_x == -1 || coords[i].X() < min_x )
|
||||||
min_x = coords[i].X();
|
min_x = coords[i].X();
|
||||||
if ( max_x == -1 || coords[i].X() > max_x )
|
if ( max_x == -1 || coords[i].X() > max_x )
|
||||||
max_x = coords[i].X();
|
max_x = coords[i].X();
|
||||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||||
min_y = coords[i].Y();
|
min_y = coords[i].Y();
|
||||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||||
max_y = coords[i].Y();
|
max_y = coords[i].Y();
|
||||||
}
|
}
|
||||||
extent = Box( min_x, min_y, max_x, max_y );
|
extent = Box( min_x, min_y, max_x, max_y );
|
||||||
calcArea();
|
calcArea();
|
||||||
calcCentre();
|
calcCentre();
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
||||||
{
|
{
|
||||||
coords = new Coord[n_coords];
|
coords = new Coord[n_coords];
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_polygon.coords[i];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
if ( n_coords < p_polygon.n_coords )
|
if ( n_coords < p_polygon.n_coords )
|
||||||
{
|
{
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
coords = new Coord[p_polygon.n_coords];
|
coords = new Coord[p_polygon.n_coords];
|
||||||
}
|
}
|
||||||
n_coords = p_polygon.n_coords;
|
n_coords = p_polygon.n_coords;
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_polygon.coords[i];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
extent = p_polygon.extent;
|
extent = p_polygon.extent;
|
||||||
area = p_polygon.area;
|
area = p_polygon.area;
|
||||||
centre = p_polygon.centre;
|
centre = p_polygon.centre;
|
||||||
return( *this );
|
return( *this );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Polygon::isInside( const Coord &coord ) const
|
bool Polygon::isInside( const Coord &coord ) const
|
||||||
{
|
{
|
||||||
bool inside = false;
|
bool inside = false;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||||
{
|
{
|
||||||
inside = !inside;
|
inside = !inside;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( inside );
|
return( inside );
|
||||||
}
|
}
|
||||||
|
|
148
src/zm_poly.h
148
src/zm_poly.h
|
@ -33,93 +33,93 @@
|
||||||
class Polygon
|
class Polygon
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct Edge
|
struct Edge
|
||||||
{
|
{
|
||||||
int min_y;
|
int min_y;
|
||||||
int max_y;
|
int max_y;
|
||||||
double min_x;
|
double min_x;
|
||||||
double _1_m;
|
double _1_m;
|
||||||
|
|
||||||
static int CompareYX( const void *p1, const void *p2 )
|
static int CompareYX( const void *p1, const void *p2 )
|
||||||
{
|
{
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
if ( e1->min_y == e2->min_y )
|
if ( e1->min_y == e2->min_y )
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
else
|
else
|
||||||
return( int(e1->min_y - e2->min_y) );
|
return( int(e1->min_y - e2->min_y) );
|
||||||
}
|
}
|
||||||
static int CompareX( const void *p1, const void *p2 )
|
static int CompareX( const void *p1, const void *p2 )
|
||||||
{
|
{
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Slice
|
struct Slice
|
||||||
{
|
{
|
||||||
int min_x;
|
int min_x;
|
||||||
int max_x;
|
int max_x;
|
||||||
int n_edges;
|
int n_edges;
|
||||||
int *edges;
|
int *edges;
|
||||||
|
|
||||||
Slice()
|
Slice()
|
||||||
{
|
{
|
||||||
n_edges = 0;
|
n_edges = 0;
|
||||||
edges = 0;
|
edges = 0;
|
||||||
}
|
}
|
||||||
~Slice()
|
~Slice()
|
||||||
{
|
{
|
||||||
delete edges;
|
delete edges;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int n_coords;
|
int n_coords;
|
||||||
Coord *coords;
|
Coord *coords;
|
||||||
Box extent;
|
Box extent;
|
||||||
int area;
|
int area;
|
||||||
Coord centre;
|
Coord centre;
|
||||||
Edge *edges;
|
Edge *edges;
|
||||||
Slice *slices;
|
Slice *slices;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialiseEdges();
|
void initialiseEdges();
|
||||||
void calcArea();
|
void calcArea();
|
||||||
void calcCentre();
|
void calcCentre();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Polygon( int p_n_coords, const Coord *p_coords );
|
Polygon( int p_n_coords, const Coord *p_coords );
|
||||||
Polygon( const Polygon &p_polygon );
|
Polygon( const Polygon &p_polygon );
|
||||||
~Polygon()
|
~Polygon()
|
||||||
{
|
{
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon &operator=( const Polygon &p_polygon );
|
Polygon &operator=( const Polygon &p_polygon );
|
||||||
|
|
||||||
inline int getNumCoords() const { return( n_coords ); }
|
inline int getNumCoords() const { return( n_coords ); }
|
||||||
inline const Coord &getCoord( int index ) const
|
inline const Coord &getCoord( int index ) const
|
||||||
{
|
{
|
||||||
return( coords[index] );
|
return( coords[index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Box &Extent() const { return( extent ); }
|
inline const Box &Extent() const { return( extent ); }
|
||||||
inline int LoX() const { return( extent.LoX() ); }
|
inline int LoX() const { return( extent.LoX() ); }
|
||||||
inline int HiX() const { return( extent.HiX() ); }
|
inline int HiX() const { return( extent.HiX() ); }
|
||||||
inline int LoY() const { return( extent.LoY() ); }
|
inline int LoY() const { return( extent.LoY() ); }
|
||||||
inline int HiY() const { return( extent.HiY() ); }
|
inline int HiY() const { return( extent.HiY() ); }
|
||||||
inline int Width() const { return( extent.Width() ); }
|
inline int Width() const { return( extent.Width() ); }
|
||||||
inline int Height() const { return( extent.Height() ); }
|
inline int Height() const { return( extent.Height() ); }
|
||||||
|
|
||||||
inline int Area() const { return( area ); }
|
inline int Area() const { return( area ); }
|
||||||
inline const Coord &Centre() const
|
inline const Coord &Centre() const
|
||||||
{
|
{
|
||||||
return( centre );
|
return( centre );
|
||||||
}
|
}
|
||||||
bool isInside( const Coord &coord ) const;
|
bool isInside( const Coord &coord ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_POLY_H
|
#endif // ZM_POLY_H
|
||||||
|
|
|
@ -26,99 +26,99 @@
|
||||||
|
|
||||||
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
||||||
{
|
{
|
||||||
const char *errstr;
|
const char *errstr;
|
||||||
int erroffset = 0;
|
int erroffset = 0;
|
||||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||||
{
|
{
|
||||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||||
}
|
}
|
||||||
|
|
||||||
regextra = pcre_study( regex, 0, &errstr );
|
regextra = pcre_study( regex, 0, &errstr );
|
||||||
if ( errstr )
|
if ( errstr )
|
||||||
{
|
{
|
||||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (ok = (bool)regex) )
|
if ( (ok = (bool)regex) )
|
||||||
{
|
{
|
||||||
match_vectors = new int[3*max_matches];
|
match_vectors = new int[3*max_matches];
|
||||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||||
match_buffers = new char *[max_matches];
|
match_buffers = new char *[max_matches];
|
||||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||||
match_lengths = new int[max_matches];
|
match_lengths = new int[max_matches];
|
||||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||||
match_valid = new bool[max_matches];
|
match_valid = new bool[max_matches];
|
||||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||||
}
|
}
|
||||||
n_matches = 0;
|
n_matches = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpr::~RegExpr()
|
RegExpr::~RegExpr()
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < max_matches; i++ )
|
for ( int i = 0; i < max_matches; i++ )
|
||||||
{
|
{
|
||||||
if ( match_buffers[i] )
|
if ( match_buffers[i] )
|
||||||
{
|
{
|
||||||
delete[] match_buffers[i];
|
delete[] match_buffers[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete[] match_valid;
|
delete[] match_valid;
|
||||||
delete[] match_lengths;
|
delete[] match_lengths;
|
||||||
delete[] match_buffers;
|
delete[] match_buffers;
|
||||||
delete[] match_vectors;
|
delete[] match_vectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
||||||
{
|
{
|
||||||
match_string = subject_string;
|
match_string = subject_string;
|
||||||
|
|
||||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||||
|
|
||||||
if ( n_matches <= 0 )
|
if ( n_matches <= 0 )
|
||||||
{
|
{
|
||||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||||
{
|
{
|
||||||
Error( "Error %d executing regular expression", n_matches );
|
Error( "Error %d executing regular expression", n_matches );
|
||||||
}
|
}
|
||||||
return( n_matches = 0 );
|
return( n_matches = 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( int i = 0; i < max_matches; i++ )
|
for( int i = 0; i < max_matches; i++ )
|
||||||
{
|
{
|
||||||
match_valid[i] = false;
|
match_valid[i] = false;
|
||||||
}
|
}
|
||||||
return( n_matches );
|
return( n_matches );
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *RegExpr::MatchString( int match_index ) const
|
const char *RegExpr::MatchString( int match_index ) const
|
||||||
{
|
{
|
||||||
if ( match_index > n_matches )
|
if ( match_index > n_matches )
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
if ( !match_valid[match_index] )
|
if ( !match_valid[match_index] )
|
||||||
{
|
{
|
||||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||||
if ( match_lengths[match_index] < (match_len+1) )
|
if ( match_lengths[match_index] < (match_len+1) )
|
||||||
{
|
{
|
||||||
delete[] match_buffers[match_index];
|
delete[] match_buffers[match_index];
|
||||||
match_buffers[match_index] = new char[match_len+1];
|
match_buffers[match_index] = new char[match_len+1];
|
||||||
match_lengths[match_index] = match_len+1;
|
match_lengths[match_index] = match_len+1;
|
||||||
}
|
}
|
||||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||||
match_buffers[match_index][match_len] = '\0';
|
match_buffers[match_index][match_len] = '\0';
|
||||||
match_valid[match_index] = true;
|
match_valid[match_index] = true;
|
||||||
}
|
}
|
||||||
return( match_buffers[match_index] );
|
return( match_buffers[match_index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegExpr::MatchLength( int match_index ) const
|
int RegExpr::MatchLength( int match_index ) const
|
||||||
{
|
{
|
||||||
if ( match_index > n_matches )
|
if ( match_index > n_matches )
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBPCRE
|
#endif // HAVE_LIBPCRE
|
||||||
|
|
|
@ -35,29 +35,29 @@
|
||||||
class RegExpr
|
class RegExpr
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
pcre *regex;
|
pcre *regex;
|
||||||
pcre_extra *regextra;
|
pcre_extra *regextra;
|
||||||
int max_matches;
|
int max_matches;
|
||||||
int *match_vectors;
|
int *match_vectors;
|
||||||
mutable char **match_buffers;
|
mutable char **match_buffers;
|
||||||
int *match_lengths;
|
int *match_lengths;
|
||||||
bool *match_valid;
|
bool *match_valid;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char *match_string;
|
const char *match_string;
|
||||||
int n_matches;
|
int n_matches;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||||
~RegExpr();
|
~RegExpr();
|
||||||
bool Ok() const { return( ok ); }
|
bool Ok() const { return( ok ); }
|
||||||
int MatchCount() const { return( n_matches ); }
|
int MatchCount() const { return( n_matches ); }
|
||||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||||
const char *MatchString( int match_index ) const;
|
const char *MatchString( int match_index ) const;
|
||||||
int MatchLength( int match_index ) const;
|
int MatchLength( int match_index ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBPCRE
|
#endif // HAVE_LIBPCRE
|
||||||
|
|
186
src/zm_rgb.h
186
src/zm_rgb.h
|
@ -20,80 +20,80 @@
|
||||||
#ifndef ZM_RGB_H
|
#ifndef ZM_RGB_H
|
||||||
#define ZM_RGB_H
|
#define ZM_RGB_H
|
||||||
|
|
||||||
typedef uint32_t Rgb; // RGB colour type
|
typedef uint32_t Rgb; // RGB colour type
|
||||||
|
|
||||||
#define WHITE 0xff
|
#define WHITE 0xff
|
||||||
#define WHITE_R 0xff
|
#define WHITE_R 0xff
|
||||||
#define WHITE_G 0xff
|
#define WHITE_G 0xff
|
||||||
#define WHITE_B 0xff
|
#define WHITE_B 0xff
|
||||||
|
|
||||||
#define BLACK 0x00
|
#define BLACK 0x00
|
||||||
#define BLACK_R 0x00
|
#define BLACK_R 0x00
|
||||||
#define BLACK_G 0x00
|
#define BLACK_G 0x00
|
||||||
#define BLACK_B 0x00
|
#define BLACK_B 0x00
|
||||||
|
|
||||||
#define RGB_WHITE (0x00ffffff)
|
#define RGB_WHITE (0x00ffffff)
|
||||||
#define RGB_BLACK (0x00000000)
|
#define RGB_BLACK (0x00000000)
|
||||||
#define RGB_RED (0x000000ff)
|
#define RGB_RED (0x000000ff)
|
||||||
#define RGB_GREEN (0x0000ff00)
|
#define RGB_GREEN (0x0000ff00)
|
||||||
#define RGB_BLUE (0x00ff0000)
|
#define RGB_BLUE (0x00ff0000)
|
||||||
#define RGB_ORANGE (0x0000a5ff)
|
#define RGB_ORANGE (0x0000a5ff)
|
||||||
#define RGB_PURPLE (0x00800080)
|
#define RGB_PURPLE (0x00800080)
|
||||||
#define RGB_TRANSPARENT (0x01000000)
|
#define RGB_TRANSPARENT (0x01000000)
|
||||||
|
|
||||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||||
|
|
||||||
/* RGB or RGBA macros */
|
/* RGB or RGBA macros */
|
||||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||||
|
|
||||||
/* BGR or BGRA */
|
/* BGR or BGRA */
|
||||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||||
|
|
||||||
/* ARGB */
|
/* ARGB */
|
||||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||||
|
|
||||||
/* ABGR */
|
/* ABGR */
|
||||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||||
|
|
||||||
|
|
||||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||||
|
|
||||||
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
||||||
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
||||||
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
||||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||||
|
|
||||||
/* ZM colours */
|
/* ZM colours */
|
||||||
#define ZM_COLOUR_RGB32 4
|
#define ZM_COLOUR_RGB32 4
|
||||||
|
@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type
|
||||||
|
|
||||||
/* A macro to use default subpixel order for a specified colour. */
|
/* A macro to use default subpixel order for a specified colour. */
|
||||||
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
||||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||||
|
|
||||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||||
Rgb result;
|
Rgb result;
|
||||||
|
|
||||||
switch(p_subpixorder) {
|
switch(p_subpixorder) {
|
||||||
|
|
||||||
case ZM_SUBPIX_ORDER_BGR:
|
case ZM_SUBPIX_ORDER_BGR:
|
||||||
case ZM_SUBPIX_ORDER_BGRA:
|
case ZM_SUBPIX_ORDER_BGRA:
|
||||||
{
|
{
|
||||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_SUBPIX_ORDER_ARGB:
|
case ZM_SUBPIX_ORDER_ARGB:
|
||||||
{
|
{
|
||||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_SUBPIX_ORDER_ABGR:
|
case ZM_SUBPIX_ORDER_ABGR:
|
||||||
{
|
{
|
||||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* Grayscale */
|
/* Grayscale */
|
||||||
case ZM_SUBPIX_ORDER_NONE:
|
case ZM_SUBPIX_ORDER_NONE:
|
||||||
result = p_col & 0xff;
|
result = p_col & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return p_col;
|
return p_col;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_RGB_H
|
#endif // ZM_RGB_H
|
||||||
|
|
|
@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
||||||
|
|
||||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
const RtcpPacket *rtcpPacket;
|
const RtcpPacket *rtcpPacket;
|
||||||
rtcpPacket = (RtcpPacket *)packet;
|
rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int consumed = 0;
|
int consumed = 0;
|
||||||
|
|
||||||
//printf( "C: " );
|
//printf( "C: " );
|
||||||
//for ( int i = 0; i < packetLen; i++ )
|
//for ( int i = 0; i < packetLen; i++ )
|
||||||
//printf( "%02x ", (unsigned char)packet[i] );
|
//printf( "%02x ", (unsigned char)packet[i] );
|
||||||
//printf( "\n" );
|
//printf( "\n" );
|
||||||
int ver = rtcpPacket->header.version;
|
int ver = rtcpPacket->header.version;
|
||||||
int count = rtcpPacket->header.count;
|
int count = rtcpPacket->header.count;
|
||||||
int pt = rtcpPacket->header.pt;
|
int pt = rtcpPacket->header.pt;
|
||||||
int len = ntohs(rtcpPacket->header.lenN);
|
int len = ntohs(rtcpPacket->header.lenN);
|
||||||
|
|
||||||
Debug( 5, "RTCP Ver: %d", ver );
|
Debug( 5, "RTCP Ver: %d", ver );
|
||||||
Debug( 5, "RTCP Count: %d", count );
|
Debug( 5, "RTCP Count: %d", count );
|
||||||
Debug( 5, "RTCP Pt: %d", pt );
|
Debug( 5, "RTCP Pt: %d", pt );
|
||||||
Debug( 5, "RTCP len: %d", len );
|
Debug( 5, "RTCP len: %d", len );
|
||||||
|
|
||||||
switch( pt )
|
switch( pt )
|
||||||
|
{
|
||||||
|
case RTCP_SR :
|
||||||
{
|
{
|
||||||
case RTCP_SR :
|
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||||
{
|
|
||||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
|
||||||
|
|
||||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||||
if ( mRtpSource.getSsrc() )
|
if ( mRtpSource.getSsrc() )
|
||||||
{
|
{
|
||||||
if ( ssrc != mRtpSource.getSsrc() )
|
if ( ssrc != mRtpSource.getSsrc() )
|
||||||
{
|
{
|
||||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( ssrc )
|
else if ( ssrc )
|
||||||
{
|
{
|
||||||
mRtpSource.setSsrc( ssrc );
|
mRtpSource.setSsrc( ssrc );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( len > 1 )
|
if ( len > 1 )
|
||||||
{
|
{
|
||||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||||
|
|
||||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case RTCP_SDES :
|
|
||||||
{
|
|
||||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
|
||||||
while ( contentLen )
|
|
||||||
{
|
|
||||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
|
||||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
|
||||||
|
|
||||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
|
||||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
|
||||||
{
|
|
||||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
|
||||||
for ( int i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
|
||||||
Debug( 5, "RTCP Item length %d", item->len );
|
|
||||||
switch( item->type )
|
|
||||||
{
|
|
||||||
case RTCP_SDES_CNAME :
|
|
||||||
{
|
|
||||||
std::string cname( item->data, item->len );
|
|
||||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_SDES_END :
|
|
||||||
case RTCP_SDES_NAME :
|
|
||||||
case RTCP_SDES_EMAIL :
|
|
||||||
case RTCP_SDES_PHONE :
|
|
||||||
case RTCP_SDES_LOC :
|
|
||||||
case RTCP_SDES_TOOL :
|
|
||||||
case RTCP_SDES_NOTE :
|
|
||||||
case RTCP_SDES_PRIV :
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
|
||||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
|
||||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
|
||||||
sdesPtr += paddedLen;
|
|
||||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_BYE :
|
|
||||||
{
|
|
||||||
Debug( 5, "RTCP Got BYE" );
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_APP :
|
|
||||||
{
|
|
||||||
// Ignoring as per RFC 3550
|
|
||||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_RR :
|
|
||||||
{
|
|
||||||
Error( "Received RTCP_RR packet." );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
// Ignore unknown packet types. Some cameras do this by design.
|
|
||||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
consumed = sizeof(uint32_t)*(len+1);
|
case RTCP_SDES :
|
||||||
return( consumed );
|
{
|
||||||
|
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||||
|
while ( contentLen )
|
||||||
|
{
|
||||||
|
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||||
|
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||||
|
|
||||||
|
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||||
|
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||||
|
{
|
||||||
|
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||||
|
for ( int i = 0; i < count; i++ )
|
||||||
|
{
|
||||||
|
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||||
|
Debug( 5, "RTCP Item length %d", item->len );
|
||||||
|
switch( item->type )
|
||||||
|
{
|
||||||
|
case RTCP_SDES_CNAME :
|
||||||
|
{
|
||||||
|
std::string cname( item->data, item->len );
|
||||||
|
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_SDES_END :
|
||||||
|
case RTCP_SDES_NAME :
|
||||||
|
case RTCP_SDES_EMAIL :
|
||||||
|
case RTCP_SDES_PHONE :
|
||||||
|
case RTCP_SDES_LOC :
|
||||||
|
case RTCP_SDES_TOOL :
|
||||||
|
case RTCP_SDES_NOTE :
|
||||||
|
case RTCP_SDES_PRIV :
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||||
|
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||||
|
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||||
|
sdesPtr += paddedLen;
|
||||||
|
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_BYE :
|
||||||
|
{
|
||||||
|
Debug( 5, "RTCP Got BYE" );
|
||||||
|
mStop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_APP :
|
||||||
|
{
|
||||||
|
// Ignoring as per RFC 3550
|
||||||
|
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_RR :
|
||||||
|
{
|
||||||
|
Error( "Received RTCP_RR packet." );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
// Ignore unknown packet types. Some cameras do this by design.
|
||||||
|
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumed = sizeof(uint32_t)*(len+1);
|
||||||
|
return( consumed );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_RR;
|
rtcpPacket->header.pt = RTCP_RR;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
mRtpSource.updateRtcpStats();
|
mRtpSource.updateRtcpStats();
|
||||||
|
|
||||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||||
|
|
||||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
const std::string &cname = mRtpSource.getCname();
|
const std::string &cname = mRtpSource.getCname();
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_SDES;
|
rtcpPacket->header.pt = RTCP_SDES;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_BYE;
|
rtcpPacket->header.pt = RTCP_BYE;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
||||||
{
|
{
|
||||||
unsigned char *bufferPtr = buffer;
|
unsigned char *bufferPtr = buffer;
|
||||||
|
|
||||||
// u_int32 len; /* length of compound RTCP packet in words */
|
// u_int32 len; /* length of compound RTCP packet in words */
|
||||||
// rtcp_t *r; /* RTCP header */
|
// rtcp_t *r; /* RTCP header */
|
||||||
// rtcp_t *end; /* end of compound RTCP packet */
|
// rtcp_t *end; /* end of compound RTCP packet */
|
||||||
|
|
||||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||||
// /* something wrong with packet format */
|
// /* something wrong with packet format */
|
||||||
// }
|
// }
|
||||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||||
|
|
||||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||||
// while (r < end && r->common.version == 2);
|
// while (r < end && r->common.version == 2);
|
||||||
|
|
||||||
// if (r != end) {
|
// if (r != end) {
|
||||||
// /* something wrong with packet format */
|
// /* something wrong with packet format */
|
||||||
// }
|
// }
|
||||||
|
|
||||||
while ( nBytes > 0 )
|
while ( nBytes > 0 )
|
||||||
{
|
{
|
||||||
int consumed = recvPacket( bufferPtr, nBytes );
|
int consumed = recvPacket( bufferPtr, nBytes );
|
||||||
if ( consumed <= 0 )
|
if ( consumed <= 0 )
|
||||||
break;
|
break;
|
||||||
bufferPtr += consumed;
|
bufferPtr += consumed;
|
||||||
nBytes -= consumed;
|
nBytes -= consumed;
|
||||||
}
|
}
|
||||||
return( nBytes );
|
return( nBytes );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::run()
|
int RtpCtrlThread::run()
|
||||||
{
|
{
|
||||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||||
SockAddrInet localAddr, remoteAddr;
|
SockAddrInet localAddr, remoteAddr;
|
||||||
|
|
||||||
bool sendReports;
|
bool sendReports;
|
||||||
UdpInetSocket rtpCtrlServer;
|
UdpInetSocket rtpCtrlServer;
|
||||||
if ( mRtpSource.getLocalHost() != "" )
|
if ( mRtpSource.getLocalHost() != "" )
|
||||||
|
{
|
||||||
|
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||||
|
Fatal( "Failed to bind RTCP server" );
|
||||||
|
sendReports = false;
|
||||||
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||||
|
Fatal( "Failed to bind RTCP server" );
|
||||||
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||||
|
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||||
|
Fatal( "Failed to connect RTCP server" );
|
||||||
|
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||||
|
sendReports = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||||
|
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||||
|
Select select( 10 );
|
||||||
|
select.addReader( &rtpCtrlServer );
|
||||||
|
|
||||||
|
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||||
|
|
||||||
|
time_t last_receive = time(NULL);
|
||||||
|
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||||
|
|
||||||
|
while ( !mStop && select.wait() >= 0 ) {
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
Select::CommsList readable = select.getReadable();
|
||||||
|
if ( readable.size() == 0 )
|
||||||
{
|
{
|
||||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
if ( ! timeout ) {
|
||||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||||
Fatal( "Failed to bind RTCP server" );
|
ssize_t nBytes;
|
||||||
sendReports = false;
|
unsigned char *bufferPtr = buffer;
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
|
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
|
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||||
|
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||||
|
Error( "Unable to send: %s", strerror( errno ) );
|
||||||
|
timeout = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
//Error( "RTCP timed out" );
|
||||||
|
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||||
|
continue;
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timeout = false;
|
||||||
|
last_receive = time(NULL);
|
||||||
}
|
}
|
||||||
else
|
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||||
{
|
{
|
||||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
{
|
||||||
Fatal( "Failed to bind RTCP server" );
|
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
|
||||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
|
||||||
Fatal( "Failed to connect RTCP server" );
|
|
||||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
|
||||||
sendReports = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
if ( nBytes )
|
||||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
|
||||||
Select select( 10 );
|
|
||||||
select.addReader( &rtpCtrlServer );
|
|
||||||
|
|
||||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
|
||||||
|
|
||||||
time_t last_receive = time(NULL);
|
|
||||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
|
||||||
|
|
||||||
while ( !mStop && select.wait() >= 0 ) {
|
|
||||||
|
|
||||||
time_t now = time(NULL);
|
|
||||||
Select::CommsList readable = select.getReadable();
|
|
||||||
if ( readable.size() == 0 )
|
|
||||||
{
|
{
|
||||||
if ( ! timeout ) {
|
recvPackets( buffer, nBytes );
|
||||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
|
||||||
ssize_t nBytes;
|
|
||||||
unsigned char *bufferPtr = buffer;
|
|
||||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
|
||||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
|
||||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
|
||||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
|
||||||
Error( "Unable to send: %s", strerror( errno ) );
|
|
||||||
timeout = true;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
//Error( "RTCP timed out" );
|
|
||||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
|
||||||
continue;
|
|
||||||
//break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeout = false;
|
|
||||||
last_receive = time(NULL);
|
|
||||||
}
|
|
||||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
|
||||||
{
|
|
||||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
|
||||||
{
|
|
||||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
|
||||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
|
||||||
|
|
||||||
if ( nBytes )
|
if ( sendReports )
|
||||||
{
|
{
|
||||||
recvPackets( buffer, nBytes );
|
unsigned char *bufferPtr = buffer;
|
||||||
|
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
if ( sendReports )
|
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
{
|
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||||
unsigned char *bufferPtr = buffer;
|
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
Error( "Unable to send: %s", strerror( errno ) );
|
||||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
}
|
||||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
} else {
|
||||||
Error( "Unable to send: %s", strerror( errno ) );
|
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
mStop = true;
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Panic( "Barfed" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Panic( "Barfed" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rtpCtrlServer.close();
|
}
|
||||||
mRtspThread.stop();
|
rtpCtrlServer.close();
|
||||||
return( 0 );
|
mRtspThread.stop();
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "zm_thread.h"
|
#include "zm_thread.h"
|
||||||
|
|
||||||
// Defined in ffmpeg rtp.h
|
// Defined in ffmpeg rtp.h
|
||||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||||
|
|
||||||
// Big-endian mask for version, padding bit and packet type pair
|
// Big-endian mask for version, padding bit and packet type pair
|
||||||
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
||||||
|
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
|
||||||
friend class RtspThread;
|
friend class RtspThread;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum
|
typedef enum
|
||||||
|
{
|
||||||
|
RTCP_SR = 200,
|
||||||
|
RTCP_RR = 201,
|
||||||
|
RTCP_SDES = 202,
|
||||||
|
RTCP_BYE = 203,
|
||||||
|
RTCP_APP = 204
|
||||||
|
} RtcpType;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
RTCP_SDES_END = 0,
|
||||||
|
RTCP_SDES_CNAME = 1,
|
||||||
|
RTCP_SDES_NAME = 2,
|
||||||
|
RTCP_SDES_EMAIL = 3,
|
||||||
|
RTCP_SDES_PHONE = 4,
|
||||||
|
RTCP_SDES_LOC = 5,
|
||||||
|
RTCP_SDES_TOOL = 6,
|
||||||
|
RTCP_SDES_NOTE = 7,
|
||||||
|
RTCP_SDES_PRIV = 8
|
||||||
|
} RtcpSdesType;
|
||||||
|
|
||||||
|
struct RtcpCommonHeader
|
||||||
|
{
|
||||||
|
uint8_t count:5; // varies by packet type
|
||||||
|
uint8_t p:1; // padding flag
|
||||||
|
uint8_t version:2; // protocol version
|
||||||
|
uint8_t pt; // RTCP packet type
|
||||||
|
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reception report block
|
||||||
|
struct RtcpRr
|
||||||
|
{
|
||||||
|
uint32_t ssrcN; // data source being reported
|
||||||
|
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||||
|
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||||
|
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||||
|
uint32_t jitterN; // interarrival jitter, network order
|
||||||
|
uint32_t lsrN; // last SR packet from this source, network order
|
||||||
|
uint32_t dlsrN; // delay since last SR packet, network order
|
||||||
|
};
|
||||||
|
|
||||||
|
// SDES item
|
||||||
|
struct RtcpSdesItem
|
||||||
|
{
|
||||||
|
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||||
|
uint8_t len; // length of item (in octets)
|
||||||
|
char data[]; // text, not null-terminated
|
||||||
|
};
|
||||||
|
|
||||||
|
// RTCP packet
|
||||||
|
struct RtcpPacket
|
||||||
|
{
|
||||||
|
RtcpCommonHeader header; // common header
|
||||||
|
union
|
||||||
{
|
{
|
||||||
RTCP_SR = 200,
|
// Sender Report (SR)
|
||||||
RTCP_RR = 201,
|
struct Sr
|
||||||
RTCP_SDES = 202,
|
{
|
||||||
RTCP_BYE = 203,
|
uint32_t ssrcN; // sender generating this report, network order
|
||||||
RTCP_APP = 204
|
uint32_t ntpSecN; // NTP timestamp, network order
|
||||||
} RtcpType;
|
uint32_t ntpFracN;
|
||||||
|
uint32_t rtpTsN; // RTP timestamp, network order
|
||||||
|
uint32_t pSentN; // packets sent, network order
|
||||||
|
uint32_t oSentN; // octets sent, network order
|
||||||
|
RtcpRr rr[]; // variable-length list
|
||||||
|
} sr;
|
||||||
|
|
||||||
typedef enum
|
// Reception Report (RR)
|
||||||
{
|
struct Rr
|
||||||
RTCP_SDES_END = 0,
|
{
|
||||||
RTCP_SDES_CNAME = 1,
|
uint32_t ssrcN; // receiver generating this report
|
||||||
RTCP_SDES_NAME = 2,
|
RtcpRr rr[]; // variable-length list
|
||||||
RTCP_SDES_EMAIL = 3,
|
} rr;
|
||||||
RTCP_SDES_PHONE = 4,
|
|
||||||
RTCP_SDES_LOC = 5,
|
|
||||||
RTCP_SDES_TOOL = 6,
|
|
||||||
RTCP_SDES_NOTE = 7,
|
|
||||||
RTCP_SDES_PRIV = 8
|
|
||||||
} RtcpSdesType;
|
|
||||||
|
|
||||||
struct RtcpCommonHeader
|
// source description (SDES)
|
||||||
{
|
struct Sdes
|
||||||
uint8_t count:5; // varies by packet type
|
{
|
||||||
uint8_t p:1; // padding flag
|
uint32_t srcN; // first SSRC/CSRC
|
||||||
uint8_t version:2; // protocol version
|
RtcpSdesItem item[]; // list of SDES items
|
||||||
uint8_t pt; // RTCP packet type
|
} sdes;
|
||||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reception report block
|
// BYE
|
||||||
struct RtcpRr
|
struct Bye
|
||||||
{
|
{
|
||||||
uint32_t ssrcN; // data source being reported
|
uint32_t srcN[]; // list of sources
|
||||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
} bye;
|
||||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
} body;
|
||||||
uint32_t jitterN; // interarrival jitter, network order
|
};
|
||||||
uint32_t lsrN; // last SR packet from this source, network order
|
|
||||||
uint32_t dlsrN; // delay since last SR packet, network order
|
|
||||||
};
|
|
||||||
|
|
||||||
// SDES item
|
|
||||||
struct RtcpSdesItem
|
|
||||||
{
|
|
||||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
|
||||||
uint8_t len; // length of item (in octets)
|
|
||||||
char data[]; // text, not null-terminated
|
|
||||||
};
|
|
||||||
|
|
||||||
// RTCP packet
|
|
||||||
struct RtcpPacket
|
|
||||||
{
|
|
||||||
RtcpCommonHeader header; // common header
|
|
||||||
union
|
|
||||||
{
|
|
||||||
// Sender Report (SR)
|
|
||||||
struct Sr
|
|
||||||
{
|
|
||||||
uint32_t ssrcN; // sender generating this report, network order
|
|
||||||
uint32_t ntpSecN; // NTP timestamp, network order
|
|
||||||
uint32_t ntpFracN;
|
|
||||||
uint32_t rtpTsN; // RTP timestamp, network order
|
|
||||||
uint32_t pSentN; // packets sent, network order
|
|
||||||
uint32_t oSentN; // octets sent, network order
|
|
||||||
RtcpRr rr[]; // variable-length list
|
|
||||||
} sr;
|
|
||||||
|
|
||||||
// Reception Report (RR)
|
|
||||||
struct Rr
|
|
||||||
{
|
|
||||||
uint32_t ssrcN; // receiver generating this report
|
|
||||||
RtcpRr rr[]; // variable-length list
|
|
||||||
} rr;
|
|
||||||
|
|
||||||
// source description (SDES)
|
|
||||||
struct Sdes
|
|
||||||
{
|
|
||||||
uint32_t srcN; // first SSRC/CSRC
|
|
||||||
RtcpSdesItem item[]; // list of SDES items
|
|
||||||
} sdes;
|
|
||||||
|
|
||||||
// BYE
|
|
||||||
struct Bye
|
|
||||||
{
|
|
||||||
uint32_t srcN[]; // list of sources
|
|
||||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
|
||||||
} bye;
|
|
||||||
} body;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtspThread &mRtspThread;
|
RtspThread &mRtspThread;
|
||||||
RtpSource &mRtpSource;
|
RtpSource &mRtpSource;
|
||||||
int mPort;
|
int mPort;
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTP_CTRL_H
|
#endif // ZM_RTP_CTRL_H
|
||||||
|
|
|
@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
||||||
|
|
||||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
||||||
{
|
{
|
||||||
const RtpDataHeader *rtpHeader;
|
const RtpDataHeader *rtpHeader;
|
||||||
rtpHeader = (RtpDataHeader *)packet;
|
rtpHeader = (RtpDataHeader *)packet;
|
||||||
|
|
||||||
//printf( "D: " );
|
//printf( "D: " );
|
||||||
//for ( int i = 0; i < 32; i++ )
|
//for ( int i = 0; i < 32; i++ )
|
||||||
//printf( "%02x ", (unsigned char)packet[i] );
|
//printf( "%02x ", (unsigned char)packet[i] );
|
||||||
//printf( "\n" );
|
//printf( "\n" );
|
||||||
|
|
||||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||||
Debug( 5, "P: %d", rtpHeader->p );
|
Debug( 5, "P: %d", rtpHeader->p );
|
||||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||||
|
|
||||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||||
|
|
||||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||||
{
|
{
|
||||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpDataThread::run()
|
int RtpDataThread::run()
|
||||||
{
|
{
|
||||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||||
|
|
||||||
SockAddrInet localAddr;
|
SockAddrInet localAddr;
|
||||||
UdpInetServer rtpDataSocket;
|
UdpInetServer rtpDataSocket;
|
||||||
if ( mRtpSource.getLocalHost() != "" )
|
if ( mRtpSource.getLocalHost() != "" )
|
||||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||||
else
|
else
|
||||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||||
if ( !rtpDataSocket.bind( localAddr ) )
|
if ( !rtpDataSocket.bind( localAddr ) )
|
||||||
Fatal( "Failed to bind RTP server" );
|
Fatal( "Failed to bind RTP server" );
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||||
|
|
||||||
Select select( 3 );
|
Select select( 3 );
|
||||||
select.addReader( &rtpDataSocket );
|
select.addReader( &rtpDataSocket );
|
||||||
|
|
||||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||||
while ( !mStop && select.wait() >= 0 )
|
while ( !mStop && select.wait() >= 0 )
|
||||||
{
|
{
|
||||||
if ( mStop )
|
if ( mStop )
|
||||||
break;
|
break;
|
||||||
Select::CommsList readable = select.getReadable();
|
Select::CommsList readable = select.getReadable();
|
||||||
if ( readable.size() == 0 )
|
if ( readable.size() == 0 )
|
||||||
|
{
|
||||||
|
Error( "RTP timed out" );
|
||||||
|
mStop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||||
|
{
|
||||||
|
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||||
|
{
|
||||||
|
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||||
|
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||||
|
if ( nBytes )
|
||||||
{
|
{
|
||||||
Error( "RTP timed out" );
|
recvPacket( buffer, nBytes );
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
else
|
||||||
{
|
{
|
||||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
mStop = true;
|
||||||
{
|
break;
|
||||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
|
||||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
|
||||||
if ( nBytes )
|
|
||||||
{
|
|
||||||
recvPacket( buffer, nBytes );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Panic( "Barfed" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtpDataSocket.close();
|
else
|
||||||
mRtspThread.stop();
|
{
|
||||||
return( 0 );
|
Panic( "Barfed" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtpDataSocket.close();
|
||||||
|
mRtspThread.stop();
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -30,16 +30,16 @@ class RtpSource;
|
||||||
|
|
||||||
struct RtpDataHeader
|
struct RtpDataHeader
|
||||||
{
|
{
|
||||||
uint8_t cc:4; // CSRC count
|
uint8_t cc:4; // CSRC count
|
||||||
uint8_t x:1; // header extension flag
|
uint8_t x:1; // header extension flag
|
||||||
uint8_t p:1; // padding flag
|
uint8_t p:1; // padding flag
|
||||||
uint8_t version:2; // protocol version
|
uint8_t version:2; // protocol version
|
||||||
uint8_t pt:7; // payload type
|
uint8_t pt:7; // payload type
|
||||||
uint8_t m:1; // marker bit
|
uint8_t m:1; // marker bit
|
||||||
uint16_t seqN; // sequence number, network order
|
uint16_t seqN; // sequence number, network order
|
||||||
uint32_t timestampN; // timestamp, network order
|
uint32_t timestampN; // timestamp, network order
|
||||||
uint32_t ssrcN; // synchronization source, network order
|
uint32_t ssrcN; // synchronization source, network order
|
||||||
uint32_t csrc[]; // optional CSRC list
|
uint32_t csrc[]; // optional CSRC list
|
||||||
};
|
};
|
||||||
|
|
||||||
class RtpDataThread : public Thread
|
class RtpDataThread : public Thread
|
||||||
|
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
|
||||||
friend class RtspThread;
|
friend class RtspThread;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtspThread &mRtspThread;
|
RtspThread &mRtspThread;
|
||||||
RtpSource &mRtpSource;
|
RtpSource &mRtpSource;
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTP_DATA_H
|
#endif // ZM_RTP_DATA_H
|
||||||
|
|
|
@ -35,152 +35,152 @@ struct RtpDataHeader;
|
||||||
class RtpSource
|
class RtpSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||||
private:
|
private:
|
||||||
static const int RTP_SEQ_MOD = 1<<16;
|
static const int RTP_SEQ_MOD = 1<<16;
|
||||||
static const int MAX_DROPOUT = 3000;
|
static const int MAX_DROPOUT = 3000;
|
||||||
static const int MAX_MISORDER = 100;
|
static const int MAX_MISORDER = 100;
|
||||||
static const int MIN_SEQUENTIAL = 2;
|
static const int MIN_SEQUENTIAL = 2;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Identity
|
// Identity
|
||||||
int mId; // General id (usually monitor id)
|
int mId; // General id (usually monitor id)
|
||||||
std::string mCname; // Canonical name, for SDES
|
std::string mCname; // Canonical name, for SDES
|
||||||
|
|
||||||
// RTP/RTCP fields
|
// RTP/RTCP fields
|
||||||
uint32_t mSsrc;
|
uint32_t mSsrc;
|
||||||
uint16_t mMaxSeq; // highest seq. number seen
|
uint16_t mMaxSeq; // highest seq. number seen
|
||||||
uint32_t mCycles; // shifted count of seq. number cycles
|
uint32_t mCycles; // shifted count of seq. number cycles
|
||||||
uint32_t mBaseSeq; // base seq number
|
uint32_t mBaseSeq; // base seq number
|
||||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||||
uint32_t mProbation; // sequ. packets till source is valid
|
uint32_t mProbation; // sequ. packets till source is valid
|
||||||
uint32_t mReceivedPackets; // packets received
|
uint32_t mReceivedPackets; // packets received
|
||||||
uint32_t mExpectedPrior; // packet expected at last interval
|
uint32_t mExpectedPrior; // packet expected at last interval
|
||||||
uint32_t mReceivedPrior; // packet received at last interval
|
uint32_t mReceivedPrior; // packet received at last interval
|
||||||
uint32_t mTransit; // relative trans time for prev pkt
|
uint32_t mTransit; // relative trans time for prev pkt
|
||||||
uint32_t mJitter; // estimated jitter
|
uint32_t mJitter; // estimated jitter
|
||||||
|
|
||||||
// Ports/Channels
|
// Ports/Channels
|
||||||
std::string mLocalHost;
|
std::string mLocalHost;
|
||||||
int mLocalPortChans[2];
|
int mLocalPortChans[2];
|
||||||
std::string mRemoteHost;
|
std::string mRemoteHost;
|
||||||
int mRemotePortChans[2];
|
int mRemotePortChans[2];
|
||||||
|
|
||||||
// Time keys
|
// Time keys
|
||||||
uint32_t mRtpClock;
|
uint32_t mRtpClock;
|
||||||
uint32_t mRtpFactor;
|
uint32_t mRtpFactor;
|
||||||
struct timeval mBaseTimeReal;
|
struct timeval mBaseTimeReal;
|
||||||
struct timeval mBaseTimeNtp;
|
struct timeval mBaseTimeNtp;
|
||||||
uint32_t mBaseTimeRtp;
|
uint32_t mBaseTimeRtp;
|
||||||
|
|
||||||
struct timeval mLastSrTimeReal;
|
struct timeval mLastSrTimeReal;
|
||||||
uint32_t mLastSrTimeNtpSecs;
|
uint32_t mLastSrTimeNtpSecs;
|
||||||
uint32_t mLastSrTimeNtpFrac;
|
uint32_t mLastSrTimeNtpFrac;
|
||||||
struct timeval mLastSrTimeNtp;
|
struct timeval mLastSrTimeNtp;
|
||||||
uint32_t mLastSrTimeRtp;
|
uint32_t mLastSrTimeRtp;
|
||||||
|
|
||||||
// Stats, intermittently updated
|
// Stats, intermittently updated
|
||||||
uint32_t mExpectedPackets;
|
uint32_t mExpectedPackets;
|
||||||
uint32_t mLostPackets;
|
uint32_t mLostPackets;
|
||||||
uint8_t mLostFraction;
|
uint8_t mLostFraction;
|
||||||
|
|
||||||
_AVCODECID mCodecId;
|
_AVCODECID mCodecId;
|
||||||
|
|
||||||
Buffer mFrame;
|
Buffer mFrame;
|
||||||
int mFrameCount;
|
int mFrameCount;
|
||||||
bool mFrameGood;
|
bool mFrameGood;
|
||||||
bool prevM;
|
bool prevM;
|
||||||
ThreadData<bool> mFrameReady;
|
ThreadData<bool> mFrameReady;
|
||||||
ThreadData<bool> mFrameProcessed;
|
ThreadData<bool> mFrameProcessed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init( uint16_t seq );
|
void init( uint16_t seq );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||||
|
|
||||||
bool updateSeq( uint16_t seq );
|
bool updateSeq( uint16_t seq );
|
||||||
void updateJitter( const RtpDataHeader *header );
|
void updateJitter( const RtpDataHeader *header );
|
||||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||||
void updateRtcpStats();
|
void updateRtcpStats();
|
||||||
|
|
||||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||||
|
|
||||||
uint32_t getSsrc() const
|
uint32_t getSsrc() const
|
||||||
{
|
{
|
||||||
return( mSsrc );
|
return( mSsrc );
|
||||||
}
|
}
|
||||||
void setSsrc( uint32_t ssrc )
|
void setSsrc( uint32_t ssrc )
|
||||||
{
|
{
|
||||||
mSsrc = ssrc;
|
mSsrc = ssrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getFrame( Buffer &buffer );
|
bool getFrame( Buffer &buffer );
|
||||||
|
|
||||||
const std::string &getCname() const
|
const std::string &getCname() const
|
||||||
{
|
{
|
||||||
return( mCname );
|
return( mCname );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &getLocalHost() const
|
const std::string &getLocalHost() const
|
||||||
{
|
{
|
||||||
return( mLocalHost );
|
return( mLocalHost );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLocalDataPort() const
|
int getLocalDataPort() const
|
||||||
{
|
{
|
||||||
return( mLocalPortChans[0] );
|
return( mLocalPortChans[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLocalCtrlPort() const
|
int getLocalCtrlPort() const
|
||||||
{
|
{
|
||||||
return( mLocalPortChans[1] );
|
return( mLocalPortChans[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &getRemoteHost() const
|
const std::string &getRemoteHost() const
|
||||||
{
|
{
|
||||||
return( mRemoteHost );
|
return( mRemoteHost );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRemoteDataPort() const
|
int getRemoteDataPort() const
|
||||||
{
|
{
|
||||||
return( mRemotePortChans[0] );
|
return( mRemotePortChans[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRemoteCtrlPort() const
|
int getRemoteCtrlPort() const
|
||||||
{
|
{
|
||||||
return( mRemotePortChans[1] );
|
return( mRemotePortChans[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getMaxSeq() const
|
uint32_t getMaxSeq() const
|
||||||
{
|
{
|
||||||
return( mCycles + mMaxSeq );
|
return( mCycles + mMaxSeq );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getExpectedPackets() const
|
uint32_t getExpectedPackets() const
|
||||||
{
|
{
|
||||||
return( mExpectedPackets );
|
return( mExpectedPackets );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLostPackets() const
|
uint32_t getLostPackets() const
|
||||||
{
|
{
|
||||||
return( mLostPackets );
|
return( mLostPackets );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getLostFraction() const
|
uint8_t getLostFraction() const
|
||||||
{
|
{
|
||||||
return( mLostFraction );
|
return( mLostFraction );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getJitter() const
|
uint32_t getJitter() const
|
||||||
{
|
{
|
||||||
return( mJitter >> 4 );
|
return( mJitter >> 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLastSrTimestamp() const
|
uint32_t getLastSrTimestamp() const
|
||||||
{
|
{
|
||||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
1516
src/zm_rtsp.cpp
1516
src/zm_rtsp.cpp
File diff suppressed because it is too large
Load Diff
156
src/zm_rtsp.h
156
src/zm_rtsp.h
|
@ -34,110 +34,110 @@
|
||||||
class RtspThread : public Thread
|
class RtspThread : public Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::set<int> PortSet;
|
typedef std::set<int> PortSet;
|
||||||
typedef std::set<uint32_t> SsrcSet;
|
typedef std::set<uint32_t> SsrcSet;
|
||||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int smMinDataPort;
|
static int smMinDataPort;
|
||||||
static int smMaxDataPort;
|
static int smMaxDataPort;
|
||||||
static PortSet smLocalSsrcs;
|
static PortSet smLocalSsrcs;
|
||||||
static PortSet smAssignedPorts;
|
static PortSet smAssignedPorts;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mId;
|
int mId;
|
||||||
|
|
||||||
RtspMethod mMethod;
|
RtspMethod mMethod;
|
||||||
std::string mProtocol;
|
std::string mProtocol;
|
||||||
std::string mHost;
|
std::string mHost;
|
||||||
std::string mPort;
|
std::string mPort;
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
bool mRtspDescribe;
|
bool mRtspDescribe;
|
||||||
std::string mUrl;
|
std::string mUrl;
|
||||||
|
|
||||||
// Reworked authentication system
|
// Reworked authentication system
|
||||||
// First try without authentication, even if we have a username and password
|
// First try without authentication, even if we have a username and password
|
||||||
// on receiving a 401 response, select authentication method (basic or digest)
|
// on receiving a 401 response, select authentication method (basic or digest)
|
||||||
// fill required fields and set needAuth
|
// fill required fields and set needAuth
|
||||||
// subsequent requests can set the required authentication header.
|
// subsequent requests can set the required authentication header.
|
||||||
bool mNeedAuth;
|
bool mNeedAuth;
|
||||||
int respCode;
|
int respCode;
|
||||||
zm::Authenticator* mAuthenticator;
|
zm::Authenticator* mAuthenticator;
|
||||||
|
|
||||||
|
|
||||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||||
|
|
||||||
TcpInetClient mRtspSocket;
|
TcpInetClient mRtspSocket;
|
||||||
TcpInetClient mRtspSocket2;
|
TcpInetClient mRtspSocket2;
|
||||||
|
|
||||||
SourceMap mSources;
|
SourceMap mSources;
|
||||||
|
|
||||||
SessionDescriptor *mSessDesc;
|
SessionDescriptor *mSessDesc;
|
||||||
AVFormatContext *mFormatContext;
|
AVFormatContext *mFormatContext;
|
||||||
|
|
||||||
uint16_t mSeq;
|
uint16_t mSeq;
|
||||||
uint32_t mSession;
|
uint32_t mSession;
|
||||||
uint32_t mSsrc;
|
uint32_t mSsrc;
|
||||||
|
|
||||||
int mRemotePorts[2];
|
int mRemotePorts[2];
|
||||||
int mRemoteChannels[2];
|
int mRemoteChannels[2];
|
||||||
RtspDist mDist;
|
RtspDist mDist;
|
||||||
|
|
||||||
unsigned long mRtpTime;
|
unsigned long mRtpTime;
|
||||||
|
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool sendCommand( std::string message );
|
bool sendCommand( std::string message );
|
||||||
bool recvResponse( std::string &response );
|
bool recvResponse( std::string &response );
|
||||||
void checkAuthResponse(std::string &response);
|
void checkAuthResponse(std::string &response);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||||
~RtspThread();
|
~RtspThread();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int requestPorts();
|
int requestPorts();
|
||||||
void releasePorts( int port );
|
void releasePorts( int port );
|
||||||
|
|
||||||
bool isValidSsrc( uint32_t ssrc );
|
bool isValidSsrc( uint32_t ssrc );
|
||||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||||
|
|
||||||
uint32_t getSsrc() const
|
uint32_t getSsrc() const
|
||||||
{
|
{
|
||||||
return( mSsrc );
|
return( mSsrc );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSources() const
|
bool hasSources() const
|
||||||
{
|
{
|
||||||
return( !mSources.empty() );
|
return( !mSources.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *getFormatContext()
|
AVFormatContext *getFormatContext()
|
||||||
{
|
{
|
||||||
return( mFormatContext );
|
return( mFormatContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getFrame( Buffer &frame )
|
bool getFrame( Buffer &frame )
|
||||||
{
|
{
|
||||||
SourceMap::iterator iter = mSources.begin();
|
SourceMap::iterator iter = mSources.begin();
|
||||||
if ( iter == mSources.end() )
|
if ( iter == mSources.end() )
|
||||||
return( false );
|
return( false );
|
||||||
return( iter->second->getFrame( frame ) );
|
return( iter->second->getFrame( frame ) );
|
||||||
}
|
}
|
||||||
int run();
|
int run();
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
bool stopped() const
|
bool stopped() const
|
||||||
{
|
{
|
||||||
return( mStop );
|
return( mStop );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTSP_H
|
#endif // ZM_RTSP_H
|
||||||
|
|
|
@ -28,206 +28,206 @@ namespace zm {
|
||||||
|
|
||||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal( "Unable to initialise libgcrypt" );
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
fAuthMethod = AUTH_UNDEFINED;
|
fAuthMethod = AUTH_UNDEFINED;
|
||||||
fUsername = username;
|
fUsername = username;
|
||||||
fPassword = password;
|
fPassword = password;
|
||||||
nc = 1;
|
nc = 1;
|
||||||
fCnonce = "0a4f113b";
|
fCnonce = "0a4f113b";
|
||||||
}
|
}
|
||||||
|
|
||||||
Authenticator::~Authenticator() {
|
Authenticator::~Authenticator() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::reset() {
|
void Authenticator::reset() {
|
||||||
fRealm.clear();
|
fRealm.clear();
|
||||||
fNonce.clear();
|
fNonce.clear();
|
||||||
fUsername.clear();
|
fUsername.clear();
|
||||||
fPassword.clear();
|
fPassword.clear();
|
||||||
fAuthMethod = AUTH_UNDEFINED;
|
fAuthMethod = AUTH_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::authHandleHeader(std::string headerData)
|
void Authenticator::authHandleHeader(std::string headerData)
|
||||||
{
|
{
|
||||||
const char* basic_match = "Basic ";
|
const char* basic_match = "Basic ";
|
||||||
const char* digest_match = "Digest ";
|
const char* digest_match = "Digest ";
|
||||||
size_t digest_match_len = strlen(digest_match);
|
size_t digest_match_len = strlen(digest_match);
|
||||||
|
|
||||||
// Check if basic auth
|
// Check if basic auth
|
||||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||||
|
{
|
||||||
|
fAuthMethod = AUTH_BASIC;
|
||||||
|
Debug( 2, "Set authMethod to Basic");
|
||||||
|
}
|
||||||
|
// Check if digest auth
|
||||||
|
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||||
|
{
|
||||||
|
fAuthMethod = AUTH_DIGEST;
|
||||||
|
Debug( 2, "Set authMethod to Digest");
|
||||||
|
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||||
|
// subparts are key="value"
|
||||||
|
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||||
{
|
{
|
||||||
fAuthMethod = AUTH_BASIC;
|
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||||
Debug( 2, "Set authMethod to Basic");
|
std::string key = trimSpaces( kvPair[0] );
|
||||||
}
|
if (key == "realm") {
|
||||||
// Check if digest auth
|
fRealm = trimSet( kvPair[1], "\"");
|
||||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
continue;
|
||||||
{
|
}
|
||||||
fAuthMethod = AUTH_DIGEST;
|
if (key == "nonce") {
|
||||||
Debug( 2, "Set authMethod to Digest");
|
fNonce = trimSet( kvPair[1], "\"");
|
||||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
continue;
|
||||||
// subparts are key="value"
|
}
|
||||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
if (key == "qop") {
|
||||||
{
|
fQop = trimSet( kvPair[1], "\"");
|
||||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
continue;
|
||||||
std::string key = trimSpaces( kvPair[0] );
|
}
|
||||||
if (key == "realm") {
|
|
||||||
fRealm = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == "nonce") {
|
|
||||||
fNonce = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == "qop") {
|
|
||||||
fQop = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
|
||||||
}
|
}
|
||||||
|
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::quote(std::string src)
|
std::string Authenticator::quote(std::string src)
|
||||||
{
|
{
|
||||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
||||||
{
|
{
|
||||||
std::string result = "Authorization: ";
|
std::string result = "Authorization: ";
|
||||||
if (fAuthMethod == AUTH_BASIC)
|
if (fAuthMethod == AUTH_BASIC)
|
||||||
{
|
{
|
||||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||||
|
}
|
||||||
|
else if (fAuthMethod == AUTH_DIGEST)
|
||||||
|
{
|
||||||
|
result += std::string("Digest ") +
|
||||||
|
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||||
|
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||||
|
if ( ! fQop.empty() ) {
|
||||||
|
result += ", qop=" + fQop;
|
||||||
|
result += ", nc=" + stringtf("%08x",nc);
|
||||||
|
result += ", cnonce=\"" + fCnonce + "\"";
|
||||||
}
|
}
|
||||||
else if (fAuthMethod == AUTH_DIGEST)
|
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||||
{
|
result += ", algorithm=\"MD5\"";
|
||||||
result += std::string("Digest ") +
|
|
||||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
//Authorization: Digest username="zm",
|
||||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
// realm="NC-336PW-HD-1080P",
|
||||||
if ( ! fQop.empty() ) {
|
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||||
result += ", qop=" + fQop;
|
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||||
result += ", nc=" + stringtf("%08x",nc);
|
// response="4092120557d3099a163bd51a0d59744d",
|
||||||
result += ", cnonce=\"" + fCnonce + "\"";
|
// algorithm=MD5,
|
||||||
}
|
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
// qop="auth",
|
||||||
result += ", algorithm=\"MD5\"";
|
// cnonce="c8051140765877dc",
|
||||||
|
// nc=00000001
|
||||||
//Authorization: Digest username="zm",
|
|
||||||
// realm="NC-336PW-HD-1080P",
|
}
|
||||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
result += "\r\n";
|
||||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
return result;
|
||||||
// response="4092120557d3099a163bd51a0d59744d",
|
|
||||||
// algorithm=MD5,
|
|
||||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
|
||||||
// qop="auth",
|
|
||||||
// cnonce="c8051140765877dc",
|
|
||||||
// nc=00000001
|
|
||||||
|
|
||||||
}
|
|
||||||
result += "\r\n";
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
// The "response" field is computed as:
|
// The "response" field is computed as:
|
||||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||||
size_t md5len = 16;
|
size_t md5len = 16;
|
||||||
unsigned char md5buf[md5len];
|
unsigned char md5buf[md5len];
|
||||||
char md5HexBuf[md5len*2+1];
|
char md5HexBuf[md5len*2+1];
|
||||||
|
|
||||||
// Step 1: md5(<username>:<realm>:<password>)
|
// Step 1: md5(<username>:<realm>:<password>)
|
||||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
std::string ha1Hash = md5HexBuf;
|
std::string ha1Hash = md5HexBuf;
|
||||||
|
|
||||||
// Step 2: md5(<cmd>:<url>)
|
// Step 2: md5(<cmd>:<url>)
|
||||||
std::string ha2Data = method + ":" + uri;
|
std::string ha2Data = method + ":" + uri;
|
||||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
std::string ha2Hash = md5HexBuf;
|
std::string ha2Hash = md5HexBuf;
|
||||||
|
|
||||||
// Step 3: md5(ha1:<nonce>:ha2)
|
// Step 3: md5(ha1:<nonce>:ha2)
|
||||||
std::string digestData = ha1Hash + ":" + nonce();
|
std::string digestData = ha1Hash + ":" + nonce();
|
||||||
if ( ! fQop.empty() ) {
|
if ( ! fQop.empty() ) {
|
||||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||||
nc ++;
|
nc ++;
|
||||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||||
}
|
}
|
||||||
digestData += ":" + ha2Hash;
|
digestData += ":" + ha2Hash;
|
||||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
|
|
||||||
return md5HexBuf;
|
return md5HexBuf;
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5
|
||||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::checkAuthResponse(std::string &response) {
|
void Authenticator::checkAuthResponse(std::string &response) {
|
||||||
std::string authLine;
|
std::string authLine;
|
||||||
StringVector lines = split( response, "\r\n" );
|
StringVector lines = split( response, "\r\n" );
|
||||||
const char* authenticate_match = "WWW-Authenticate:";
|
const char* authenticate_match = "WWW-Authenticate:";
|
||||||
size_t authenticate_match_len = strlen(authenticate_match);
|
size_t authenticate_match_len = strlen(authenticate_match);
|
||||||
|
|
||||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||||
// stop at end of headers
|
// stop at end of headers
|
||||||
if (lines[i].length()==0)
|
if (lines[i].length()==0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||||
authLine = lines[i];
|
authLine = lines[i];
|
||||||
Debug( 2, "Found auth line at %d", i);
|
Debug( 2, "Found auth line at %d", i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!authLine.empty()) {
|
if (!authLine.empty()) {
|
||||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||||
} else {
|
} else {
|
||||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zm
|
} // namespace zm
|
||||||
|
|
|
@ -37,20 +37,20 @@ namespace zm {
|
||||||
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
||||||
class Authenticator {
|
class Authenticator {
|
||||||
public:
|
public:
|
||||||
Authenticator(std::string &username, std::string password);
|
Authenticator(std::string &username, std::string password);
|
||||||
virtual ~Authenticator();
|
virtual ~Authenticator();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
std::string realm() { return fRealm; }
|
std::string realm() { return fRealm; }
|
||||||
std::string nonce() { return fNonce; }
|
std::string nonce() { return fNonce; }
|
||||||
std::string username() { return fUsername; }
|
std::string username() { return fUsername; }
|
||||||
AuthMethod auth_method() const { return fAuthMethod; }
|
AuthMethod auth_method() const { return fAuthMethod; }
|
||||||
|
|
||||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||||
void authHandleHeader( std::string headerData );
|
void authHandleHeader( std::string headerData );
|
||||||
std::string getAuthHeader( std::string method, std::string path );
|
std::string getAuthHeader( std::string method, std::string path );
|
||||||
void checkAuthResponse(std::string &response);
|
void checkAuthResponse(std::string &response);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string password() { return fPassword; }
|
std::string password() { return fPassword; }
|
||||||
AuthMethod fAuthMethod;
|
AuthMethod fAuthMethod;
|
||||||
|
@ -61,7 +61,7 @@ private:
|
||||||
std::string fUsername;
|
std::string fUsername;
|
||||||
std::string fPassword;
|
std::string fPassword;
|
||||||
std::string quote( std::string src );
|
std::string quote( std::string src );
|
||||||
int nc;
|
int nc;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zm
|
} // namespace zm
|
||||||
|
|
830
src/zm_sdp.cpp
830
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))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
||||||
mTtl( 16 ),
|
mTtl( 16 ),
|
||||||
mNoAddresses( 0 )
|
mNoAddresses( 0 )
|
||||||
{
|
{
|
||||||
StringVector tokens = split( connInfo, " " );
|
StringVector tokens = split( connInfo, " " );
|
||||||
if ( tokens.size() < 3 )
|
if ( tokens.size() < 3 )
|
||||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||||
mNetworkType = tokens[0];
|
mNetworkType = tokens[0];
|
||||||
if ( mNetworkType != "IN" )
|
if ( mNetworkType != "IN" )
|
||||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||||
mAddressType = tokens[1];
|
mAddressType = tokens[1];
|
||||||
if ( mAddressType != "IP4" )
|
if ( mAddressType != "IP4" )
|
||||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||||
StringVector addressTokens = split( tokens[2], "/" );
|
StringVector addressTokens = split( tokens[2], "/" );
|
||||||
if ( addressTokens.size() < 1 )
|
if ( addressTokens.size() < 1 )
|
||||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||||
mAddress = addressTokens[0];
|
mAddress = addressTokens[0];
|
||||||
if ( addressTokens.size() >= 2 )
|
if ( addressTokens.size() >= 2 )
|
||||||
mTtl = atoi(addressTokens[1].c_str());
|
mTtl = atoi(addressTokens[1].c_str());
|
||||||
if ( addressTokens.size() >= 3 )
|
if ( addressTokens.size() >= 3 )
|
||||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
||||||
mValue( 0 )
|
mValue( 0 )
|
||||||
{
|
{
|
||||||
StringVector tokens = split( bandInfo, ":" );
|
StringVector tokens = split( bandInfo, ":" );
|
||||||
if ( tokens.size() < 2 )
|
if ( tokens.size() < 2 )
|
||||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||||
mType = tokens[0];
|
mType = tokens[0];
|
||||||
//if ( mNetworkType != "IN" )
|
//if ( mNetworkType != "IN" )
|
||||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||||
mValue = atoi(tokens[1].c_str());
|
mValue = atoi(tokens[1].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||||
mType( type ),
|
mType( type ),
|
||||||
mPort( port ),
|
mPort( port ),
|
||||||
mNumPorts( numPorts ),
|
mNumPorts( numPorts ),
|
||||||
mTransport( transport ),
|
mTransport( transport ),
|
||||||
mPayloadType( payloadType ),
|
mPayloadType( payloadType ),
|
||||||
mFrameRate( 0.0 ),
|
mFrameRate( 0.0 ),
|
||||||
mClock( 0 ),
|
mClock( 0 ),
|
||||||
mWidth( 0 ),
|
mWidth( 0 ),
|
||||||
mHeight( 0 ),
|
mHeight( 0 ),
|
||||||
mSprops( "" ),
|
mSprops( "" ),
|
||||||
mConnInfo( 0 )
|
mConnInfo( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
||||||
mUrl( url ),
|
mUrl( url ),
|
||||||
mConnInfo( 0 ),
|
mConnInfo( 0 ),
|
||||||
mBandInfo( 0 )
|
mBandInfo( 0 )
|
||||||
{
|
{
|
||||||
MediaDescriptor *currMedia = 0;
|
MediaDescriptor *currMedia = 0;
|
||||||
|
|
||||||
StringVector lines = split( sdp, "\r\n" );
|
StringVector lines = split( sdp, "\r\n" );
|
||||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||||
|
{
|
||||||
|
std::string line = *iter;
|
||||||
|
if ( line.empty() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||||
|
const char sdpType = line[0];
|
||||||
|
if ( line[1] != '=' )
|
||||||
|
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||||
|
|
||||||
|
line.erase( 0, 2 );
|
||||||
|
switch( sdpType )
|
||||||
{
|
{
|
||||||
std::string line = *iter;
|
case 'v' :
|
||||||
if ( line.empty() )
|
mVersion = line;
|
||||||
break;
|
break;
|
||||||
|
case 'o' :
|
||||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
mOwner = line;
|
||||||
const char sdpType = line[0];
|
break;
|
||||||
if ( line[1] != '=' )
|
case 's' :
|
||||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
mName = line;
|
||||||
|
break;
|
||||||
line.erase( 0, 2 );
|
case 'i' :
|
||||||
switch( sdpType )
|
mInfo = line;
|
||||||
|
break;
|
||||||
|
case 'c' :
|
||||||
|
// This prevent a memory leak if the field appears more than one time
|
||||||
|
if ( mConnInfo )
|
||||||
|
delete mConnInfo;
|
||||||
|
mConnInfo = new ConnInfo( line );
|
||||||
|
break;
|
||||||
|
case 'b' :
|
||||||
|
// This prevent a memory leak if the field appears more than one time
|
||||||
|
if ( mBandInfo )
|
||||||
|
delete mBandInfo;
|
||||||
|
mBandInfo = new BandInfo( line );
|
||||||
|
break;
|
||||||
|
case 't' :
|
||||||
|
mTimeInfo = line;
|
||||||
|
break;
|
||||||
|
case 'a' :
|
||||||
|
{
|
||||||
|
mAttributes.push_back( line );
|
||||||
|
StringVector tokens = split( line, ":", 2 );
|
||||||
|
std::string attrName = tokens[0];
|
||||||
|
if ( currMedia )
|
||||||
{
|
{
|
||||||
case 'v' :
|
if ( attrName == "control" )
|
||||||
mVersion = line;
|
{
|
||||||
break;
|
if ( tokens.size() < 2 )
|
||||||
case 'o' :
|
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
mOwner = line;
|
currMedia->setControlUrl( tokens[1] );
|
||||||
break;
|
}
|
||||||
case 's' :
|
else if ( attrName == "range" )
|
||||||
mName = line;
|
{
|
||||||
break;
|
}
|
||||||
case 'i' :
|
else if ( attrName == "rtpmap" )
|
||||||
mInfo = line;
|
{
|
||||||
break;
|
// a=rtpmap:96 MP4V-ES/90000
|
||||||
case 'c' :
|
if ( tokens.size() < 2 )
|
||||||
// This prevent a memory leak if the field appears more than one time
|
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
if ( mConnInfo )
|
StringVector attrTokens = split( tokens[1], " " );
|
||||||
delete mConnInfo;
|
int payloadType = atoi(attrTokens[0].c_str());
|
||||||
mConnInfo = new ConnInfo( line );
|
if ( payloadType != currMedia->getPayloadType() )
|
||||||
break;
|
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||||
case 'b' :
|
std::string payloadDesc = attrTokens[1];
|
||||||
// This prevent a memory leak if the field appears more than one time
|
//currMedia->setPayloadType( payloadType );
|
||||||
if ( mBandInfo )
|
if ( attrTokens.size() > 1 )
|
||||||
delete mBandInfo;
|
|
||||||
mBandInfo = new BandInfo( line );
|
|
||||||
break;
|
|
||||||
case 't' :
|
|
||||||
mTimeInfo = line;
|
|
||||||
break;
|
|
||||||
case 'a' :
|
|
||||||
{
|
{
|
||||||
mAttributes.push_back( line );
|
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||||
StringVector tokens = split( line, ":", 2 );
|
std::string payloadDesc = payloadTokens[0];
|
||||||
std::string attrName = tokens[0];
|
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||||
if ( currMedia )
|
currMedia->setPayloadDesc( payloadDesc );
|
||||||
{
|
currMedia->setClock( payloadClock );
|
||||||
if ( attrName == "control" )
|
|
||||||
{
|
|
||||||
if ( tokens.size() < 2 )
|
|
||||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
||||||
currMedia->setControlUrl( tokens[1] );
|
|
||||||
}
|
|
||||||
else if ( attrName == "range" )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if ( attrName == "rtpmap" )
|
|
||||||
{
|
|
||||||
// a=rtpmap:96 MP4V-ES/90000
|
|
||||||
if ( tokens.size() < 2 )
|
|
||||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
||||||
StringVector attrTokens = split( tokens[1], " " );
|
|
||||||
int payloadType = atoi(attrTokens[0].c_str());
|
|
||||||
if ( payloadType != currMedia->getPayloadType() )
|
|
||||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
|
||||||
std::string payloadDesc = attrTokens[1];
|
|
||||||
//currMedia->setPayloadType( payloadType );
|
|
||||||
if ( attrTokens.size() > 1 )
|
|
||||||
{
|
|
||||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
|
||||||
std::string payloadDesc = payloadTokens[0];
|
|
||||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
|
||||||
currMedia->setPayloadDesc( payloadDesc );
|
|
||||||
currMedia->setClock( payloadClock );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( attrName == "framesize" )
|
|
||||||
{
|
|
||||||
// a=framesize:96 320-240
|
|
||||||
if ( tokens.size() < 2 )
|
|
||||||
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
||||||
StringVector attrTokens = split( tokens[1], " " );
|
|
||||||
int payloadType = atoi(attrTokens[0].c_str());
|
|
||||||
if ( payloadType != currMedia->getPayloadType() )
|
|
||||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
|
||||||
//currMedia->setPayloadType( payloadType );
|
|
||||||
StringVector sizeTokens = split( attrTokens[1], "-" );
|
|
||||||
int width = atoi(sizeTokens[0].c_str());
|
|
||||||
int height = atoi(sizeTokens[1].c_str());
|
|
||||||
currMedia->setFrameSize( width, height );
|
|
||||||
}
|
|
||||||
else if ( attrName == "framerate" )
|
|
||||||
{
|
|
||||||
// a=framerate:5.0
|
|
||||||
if ( tokens.size() < 2 )
|
|
||||||
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
||||||
double frameRate = atof(tokens[1].c_str());
|
|
||||||
currMedia->setFrameRate( frameRate );
|
|
||||||
}
|
|
||||||
else if ( attrName == "fmtp" )
|
|
||||||
{
|
|
||||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
|
||||||
if ( tokens.size() < 2 )
|
|
||||||
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
||||||
StringVector attrTokens = split( tokens[1], " ", 2 );
|
|
||||||
int payloadType = atoi(attrTokens[0].c_str());
|
|
||||||
if ( payloadType != currMedia->getPayloadType() )
|
|
||||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
|
||||||
//currMedia->setPayloadType( payloadType );
|
|
||||||
if ( attrTokens.size() > 1 )
|
|
||||||
{
|
|
||||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
|
||||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
|
||||||
{
|
|
||||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
|
||||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
|
||||||
if ( attr3Tokens[0] == "profile-level-id" )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if ( attr3Tokens[0] == "config" )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
|
||||||
{
|
|
||||||
size_t t = attr2Tokens[i].find("=");
|
|
||||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
|
||||||
Debug(4, "sprop-parameter-sets value %s", c);
|
|
||||||
currMedia->setSprops(std::string(c));
|
|
||||||
}
|
|
||||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
|
||||||
{
|
|
||||||
size_t t = attr2Tokens[i].find("=");
|
|
||||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
|
||||||
Debug(4, "sprop-parameter-sets value %s", c);
|
|
||||||
currMedia->setSprops(std::string(c));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( attrName == "mpeg4-iod" )
|
|
||||||
{
|
|
||||||
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
|
||||||
}
|
|
||||||
else if ( attrName == "mpeg4-esid" )
|
|
||||||
{
|
|
||||||
// a=mpeg4-esid:201
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'm' :
|
}
|
||||||
|
else if ( attrName == "framesize" )
|
||||||
|
{
|
||||||
|
// a=framesize:96 320-240
|
||||||
|
if ( tokens.size() < 2 )
|
||||||
|
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
|
StringVector attrTokens = split( tokens[1], " " );
|
||||||
|
int payloadType = atoi(attrTokens[0].c_str());
|
||||||
|
if ( payloadType != currMedia->getPayloadType() )
|
||||||
|
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||||
|
//currMedia->setPayloadType( payloadType );
|
||||||
|
StringVector sizeTokens = split( attrTokens[1], "-" );
|
||||||
|
int width = atoi(sizeTokens[0].c_str());
|
||||||
|
int height = atoi(sizeTokens[1].c_str());
|
||||||
|
currMedia->setFrameSize( width, height );
|
||||||
|
}
|
||||||
|
else if ( attrName == "framerate" )
|
||||||
|
{
|
||||||
|
// a=framerate:5.0
|
||||||
|
if ( tokens.size() < 2 )
|
||||||
|
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
|
double frameRate = atof(tokens[1].c_str());
|
||||||
|
currMedia->setFrameRate( frameRate );
|
||||||
|
}
|
||||||
|
else if ( attrName == "fmtp" )
|
||||||
|
{
|
||||||
|
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||||
|
if ( tokens.size() < 2 )
|
||||||
|
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
|
StringVector attrTokens = split( tokens[1], " ", 2 );
|
||||||
|
int payloadType = atoi(attrTokens[0].c_str());
|
||||||
|
if ( payloadType != currMedia->getPayloadType() )
|
||||||
|
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||||
|
//currMedia->setPayloadType( payloadType );
|
||||||
|
if ( attrTokens.size() > 1 )
|
||||||
{
|
{
|
||||||
StringVector tokens = split( line, " " );
|
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||||
if ( tokens.size() < 4 )
|
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||||
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
{
|
||||||
std::string mediaType = tokens[0];
|
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||||
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||||
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
if ( attr3Tokens[0] == "profile-level-id" )
|
||||||
StringVector portTokens = split( tokens[1], "/" );
|
{
|
||||||
int mediaPort = atoi(portTokens[0].c_str());
|
}
|
||||||
int mediaNumPorts = 1;
|
else if ( attr3Tokens[0] == "config" )
|
||||||
if ( portTokens.size() > 1 )
|
{
|
||||||
mediaNumPorts = atoi(portTokens[1].c_str());
|
}
|
||||||
std::string mediaTransport = tokens[2];
|
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||||
if ( mediaTransport != "RTP/AVP" )
|
{
|
||||||
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
size_t t = attr2Tokens[i].find("=");
|
||||||
int payloadType = atoi(tokens[3].c_str());
|
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||||
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
Debug(4, "sprop-parameter-sets value %s", c);
|
||||||
mMediaList.push_back( currMedia );
|
currMedia->setSprops(std::string(c));
|
||||||
break;
|
}
|
||||||
|
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||||
|
{
|
||||||
|
size_t t = attr2Tokens[i].find("=");
|
||||||
|
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||||
|
Debug(4, "sprop-parameter-sets value %s", c);
|
||||||
|
currMedia->setSprops(std::string(c));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if ( attrName == "mpeg4-iod" )
|
||||||
|
{
|
||||||
|
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
||||||
|
}
|
||||||
|
else if ( attrName == "mpeg4-esid" )
|
||||||
|
{
|
||||||
|
// a=mpeg4-esid:201
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm' :
|
||||||
|
{
|
||||||
|
StringVector tokens = split( line, " " );
|
||||||
|
if ( tokens.size() < 4 )
|
||||||
|
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
||||||
|
std::string mediaType = tokens[0];
|
||||||
|
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
||||||
|
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
||||||
|
StringVector portTokens = split( tokens[1], "/" );
|
||||||
|
int mediaPort = atoi(portTokens[0].c_str());
|
||||||
|
int mediaNumPorts = 1;
|
||||||
|
if ( portTokens.size() > 1 )
|
||||||
|
mediaNumPorts = atoi(portTokens[1].c_str());
|
||||||
|
std::string mediaTransport = tokens[2];
|
||||||
|
if ( mediaTransport != "RTP/AVP" )
|
||||||
|
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
||||||
|
int payloadType = atoi(tokens[3].c_str());
|
||||||
|
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
||||||
|
mMediaList.push_back( currMedia );
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::~SessionDescriptor()
|
SessionDescriptor::~SessionDescriptor()
|
||||||
{
|
{
|
||||||
if ( mConnInfo )
|
if ( mConnInfo )
|
||||||
delete mConnInfo;
|
delete mConnInfo;
|
||||||
if ( mBandInfo )
|
if ( mBandInfo )
|
||||||
delete mBandInfo;
|
delete mBandInfo;
|
||||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||||
delete mMediaList[i];
|
delete mMediaList[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
||||||
{
|
{
|
||||||
AVFormatContext *formatContext = avformat_alloc_context();
|
AVFormatContext *formatContext = avformat_alloc_context();
|
||||||
|
|
||||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||||
/*
|
/*
|
||||||
if ( mName.length() )
|
if ( mName.length() )
|
||||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||||
if ( mInfo.length() )
|
if ( mInfo.length() )
|
||||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||||
*/
|
*/
|
||||||
//formatContext->nb_streams = mMediaList.size();
|
//formatContext->nb_streams = mMediaList.size();
|
||||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||||
{
|
{
|
||||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
||||||
AVStream *stream = av_new_stream( formatContext, i );
|
AVStream *stream = av_new_stream( formatContext, i );
|
||||||
#else
|
#else
|
||||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||||
stream->id = i;
|
stream->id = i;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
if ( mediaDesc->getType() == "video" )
|
if ( mediaDesc->getType() == "video" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
else if ( mediaDesc->getType() == "audio" )
|
else if ( mediaDesc->getType() == "audio" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
else if ( mediaDesc->getType() == "application" )
|
else if ( mediaDesc->getType() == "application" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||||
#else
|
#else
|
||||||
if ( mediaDesc->getType() == "video" )
|
if ( mediaDesc->getType() == "video" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||||
else if ( mediaDesc->getType() == "audio" )
|
else if ( mediaDesc->getType() == "audio" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||||
else if ( mediaDesc->getType() == "application" )
|
else if ( mediaDesc->getType() == "application" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
std::string codec_name;
|
std::string codec_name;
|
||||||
#endif
|
#endif
|
||||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||||
|
{
|
||||||
|
// Look in static table
|
||||||
|
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||||
|
{
|
||||||
|
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||||
{
|
{
|
||||||
// Look in static table
|
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
|
||||||
{
|
|
||||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
|
||||||
{
|
|
||||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||||
#else
|
#else
|
||||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||||
#endif
|
#endif
|
||||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Look in dynamic table
|
||||||
|
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||||
|
{
|
||||||
|
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||||
{
|
{
|
||||||
// Look in dynamic table
|
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
|
||||||
{
|
|
||||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
|
||||||
{
|
|
||||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||||
#else
|
#else
|
||||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||||
#endif
|
#endif
|
||||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||||
stream->codec->sample_rate = mediaDesc->getClock();
|
stream->codec->sample_rate = mediaDesc->getClock();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
|
||||||
if ( codec_name.empty() )
|
|
||||||
#else
|
|
||||||
if ( !stream->codec->codec_name[0] )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
|
||||||
//return( 0 );
|
|
||||||
}
|
|
||||||
if ( mediaDesc->getWidth() )
|
|
||||||
stream->codec->width = mediaDesc->getWidth();
|
|
||||||
if ( mediaDesc->getHeight() )
|
|
||||||
stream->codec->height = mediaDesc->getHeight();
|
|
||||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
|
||||||
{
|
|
||||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
|
||||||
stream->codec->extradata_size= 0;
|
|
||||||
stream->codec->extradata= NULL;
|
|
||||||
char pvalue[1024], *value = pvalue;
|
|
||||||
|
|
||||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
|
||||||
|
|
||||||
while (*value) {
|
|
||||||
char base64packet[1024];
|
|
||||||
uint8_t decoded_packet[1024];
|
|
||||||
uint32_t packet_size;
|
|
||||||
char *dst = base64packet;
|
|
||||||
|
|
||||||
while (*value && *value != ','
|
|
||||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
|
||||||
*dst++ = *value++;
|
|
||||||
}
|
|
||||||
*dst++ = '\0';
|
|
||||||
|
|
||||||
if (*value == ',')
|
|
||||||
value++;
|
|
||||||
|
|
||||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
|
||||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
|
||||||
if (packet_size) {
|
|
||||||
uint8_t *dest =
|
|
||||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
|
||||||
stream->codec->extradata_size +
|
|
||||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
|
||||||
if(dest) {
|
|
||||||
if(stream->codec->extradata_size) {
|
|
||||||
// av_realloc?
|
|
||||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
|
||||||
av_free(stream->codec->extradata);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
|
||||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
|
||||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
|
||||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
|
||||||
|
|
||||||
stream->codec->extradata= dest;
|
|
||||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
|
||||||
// } else {
|
|
||||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
|
||||||
// return AVERROR(ENOMEM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return( formatContext );
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
|
if ( codec_name.empty() )
|
||||||
|
#else
|
||||||
|
if ( !stream->codec->codec_name[0] )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||||
|
//return( 0 );
|
||||||
|
}
|
||||||
|
if ( mediaDesc->getWidth() )
|
||||||
|
stream->codec->width = mediaDesc->getWidth();
|
||||||
|
if ( mediaDesc->getHeight() )
|
||||||
|
stream->codec->height = mediaDesc->getHeight();
|
||||||
|
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||||
|
{
|
||||||
|
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||||
|
stream->codec->extradata_size= 0;
|
||||||
|
stream->codec->extradata= NULL;
|
||||||
|
char pvalue[1024], *value = pvalue;
|
||||||
|
|
||||||
|
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||||
|
|
||||||
|
while (*value) {
|
||||||
|
char base64packet[1024];
|
||||||
|
uint8_t decoded_packet[1024];
|
||||||
|
uint32_t packet_size;
|
||||||
|
char *dst = base64packet;
|
||||||
|
|
||||||
|
while (*value && *value != ','
|
||||||
|
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||||
|
*dst++ = *value++;
|
||||||
|
}
|
||||||
|
*dst++ = '\0';
|
||||||
|
|
||||||
|
if (*value == ',')
|
||||||
|
value++;
|
||||||
|
|
||||||
|
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||||
|
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||||
|
if (packet_size) {
|
||||||
|
uint8_t *dest =
|
||||||
|
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||||
|
stream->codec->extradata_size +
|
||||||
|
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if(dest) {
|
||||||
|
if(stream->codec->extradata_size) {
|
||||||
|
// av_realloc?
|
||||||
|
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||||
|
av_free(stream->codec->extradata);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||||
|
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||||
|
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||||
|
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
|
||||||
|
stream->codec->extradata= dest;
|
||||||
|
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||||
|
// } else {
|
||||||
|
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||||
|
// return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( formatContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
350
src/zm_sdp.h
350
src/zm_sdp.h
|
@ -34,204 +34,204 @@
|
||||||
class SessionDescriptor
|
class SessionDescriptor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||||
|
|
||||||
struct StaticPayloadDesc
|
struct StaticPayloadDesc
|
||||||
{
|
{
|
||||||
int payloadType;
|
int payloadType;
|
||||||
const char payloadName[6];
|
const char payloadName[6];
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
AVMediaType codecType;
|
AVMediaType codecType;
|
||||||
#else
|
#else
|
||||||
enum CodecType codecType;
|
enum CodecType codecType;
|
||||||
#endif
|
#endif
|
||||||
_AVCODECID codecId;
|
_AVCODECID codecId;
|
||||||
int clockRate;
|
int clockRate;
|
||||||
int autoChannels;
|
int autoChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DynamicPayloadDesc
|
struct DynamicPayloadDesc
|
||||||
{
|
{
|
||||||
const char payloadName[32];
|
const char payloadName[32];
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
AVMediaType codecType;
|
AVMediaType codecType;
|
||||||
#else
|
#else
|
||||||
enum CodecType codecType;
|
enum CodecType codecType;
|
||||||
#endif
|
#endif
|
||||||
_AVCODECID codecId;
|
_AVCODECID codecId;
|
||||||
|
|
||||||
//int clockRate;
|
//int clockRate;
|
||||||
//int autoChannels;
|
//int autoChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class ConnInfo
|
class ConnInfo
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mNetworkType;
|
std::string mNetworkType;
|
||||||
std::string mAddressType;
|
std::string mAddressType;
|
||||||
std::string mAddress;
|
std::string mAddress;
|
||||||
int mTtl;
|
int mTtl;
|
||||||
int mNoAddresses;
|
int mNoAddresses;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConnInfo( const std::string &connInfo );
|
ConnInfo( const std::string &connInfo );
|
||||||
};
|
};
|
||||||
|
|
||||||
class BandInfo
|
class BandInfo
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mType;
|
std::string mType;
|
||||||
int mValue;
|
int mValue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BandInfo( const std::string &bandInfo );
|
BandInfo( const std::string &bandInfo );
|
||||||
};
|
};
|
||||||
|
|
||||||
class MediaDescriptor
|
class MediaDescriptor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mType;
|
std::string mType;
|
||||||
int mPort;
|
int mPort;
|
||||||
int mNumPorts;
|
int mNumPorts;
|
||||||
std::string mTransport;
|
std::string mTransport;
|
||||||
int mPayloadType;
|
int mPayloadType;
|
||||||
|
|
||||||
std::string mPayloadDesc;
|
std::string mPayloadDesc;
|
||||||
std::string mControlUrl;
|
std::string mControlUrl;
|
||||||
double mFrameRate;
|
double mFrameRate;
|
||||||
int mClock;
|
int mClock;
|
||||||
int mWidth;
|
int mWidth;
|
||||||
int mHeight;
|
int mHeight;
|
||||||
std::string mSprops;
|
std::string mSprops;
|
||||||
|
|
||||||
ConnInfo *mConnInfo;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
|
||||||
|
|
||||||
const std::string &getType() const
|
|
||||||
{
|
|
||||||
return( mType );
|
|
||||||
}
|
|
||||||
int getPort() const
|
|
||||||
{
|
|
||||||
return( mPort );
|
|
||||||
}
|
|
||||||
int getNumPorts() const
|
|
||||||
{
|
|
||||||
return( mNumPorts );
|
|
||||||
}
|
|
||||||
const std::string &getTransport() const
|
|
||||||
{
|
|
||||||
return( mTransport );
|
|
||||||
}
|
|
||||||
const int getPayloadType() const
|
|
||||||
{
|
|
||||||
return( mPayloadType );
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getPayloadDesc() const
|
|
||||||
{
|
|
||||||
return( mPayloadDesc );
|
|
||||||
}
|
|
||||||
void setPayloadDesc( const std::string &payloadDesc )
|
|
||||||
{
|
|
||||||
mPayloadDesc = payloadDesc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getControlUrl() const
|
|
||||||
{
|
|
||||||
return( mControlUrl );
|
|
||||||
}
|
|
||||||
void setControlUrl( const std::string &controlUrl )
|
|
||||||
{
|
|
||||||
mControlUrl = controlUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int getClock() const
|
|
||||||
{
|
|
||||||
return( mClock );
|
|
||||||
}
|
|
||||||
void setClock( int clock )
|
|
||||||
{
|
|
||||||
mClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFrameSize( int width, int height )
|
|
||||||
{
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
}
|
|
||||||
int getWidth() const
|
|
||||||
{
|
|
||||||
return( mWidth );
|
|
||||||
}
|
|
||||||
int getHeight() const
|
|
||||||
{
|
|
||||||
return( mHeight );
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSprops(const std::string props)
|
|
||||||
{
|
|
||||||
mSprops = props;
|
|
||||||
}
|
|
||||||
const std::string getSprops() const
|
|
||||||
{
|
|
||||||
return ( mSprops );
|
|
||||||
}
|
|
||||||
const double getFrameRate() const
|
|
||||||
{
|
|
||||||
return( mFrameRate );
|
|
||||||
}
|
|
||||||
void setFrameRate( double frameRate )
|
|
||||||
{
|
|
||||||
mFrameRate = frameRate;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::vector<MediaDescriptor *> MediaList;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static StaticPayloadDesc smStaticPayloads[];
|
|
||||||
static DynamicPayloadDesc smDynamicPayloads[];
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string mUrl;
|
|
||||||
|
|
||||||
std::string mVersion;
|
|
||||||
std::string mOwner;
|
|
||||||
std::string mName;
|
|
||||||
std::string mInfo;
|
|
||||||
|
|
||||||
ConnInfo *mConnInfo;
|
ConnInfo *mConnInfo;
|
||||||
BandInfo *mBandInfo;
|
|
||||||
std::string mTimeInfo;
|
|
||||||
StringVector mAttributes;
|
|
||||||
|
|
||||||
MediaList mMediaList;
|
public:
|
||||||
|
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||||
|
|
||||||
|
const std::string &getType() const
|
||||||
|
{
|
||||||
|
return( mType );
|
||||||
|
}
|
||||||
|
int getPort() const
|
||||||
|
{
|
||||||
|
return( mPort );
|
||||||
|
}
|
||||||
|
int getNumPorts() const
|
||||||
|
{
|
||||||
|
return( mNumPorts );
|
||||||
|
}
|
||||||
|
const std::string &getTransport() const
|
||||||
|
{
|
||||||
|
return( mTransport );
|
||||||
|
}
|
||||||
|
const int getPayloadType() const
|
||||||
|
{
|
||||||
|
return( mPayloadType );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getPayloadDesc() const
|
||||||
|
{
|
||||||
|
return( mPayloadDesc );
|
||||||
|
}
|
||||||
|
void setPayloadDesc( const std::string &payloadDesc )
|
||||||
|
{
|
||||||
|
mPayloadDesc = payloadDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getControlUrl() const
|
||||||
|
{
|
||||||
|
return( mControlUrl );
|
||||||
|
}
|
||||||
|
void setControlUrl( const std::string &controlUrl )
|
||||||
|
{
|
||||||
|
mControlUrl = controlUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int getClock() const
|
||||||
|
{
|
||||||
|
return( mClock );
|
||||||
|
}
|
||||||
|
void setClock( int clock )
|
||||||
|
{
|
||||||
|
mClock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFrameSize( int width, int height )
|
||||||
|
{
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
}
|
||||||
|
int getWidth() const
|
||||||
|
{
|
||||||
|
return( mWidth );
|
||||||
|
}
|
||||||
|
int getHeight() const
|
||||||
|
{
|
||||||
|
return( mHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSprops(const std::string props)
|
||||||
|
{
|
||||||
|
mSprops = props;
|
||||||
|
}
|
||||||
|
const std::string getSprops() const
|
||||||
|
{
|
||||||
|
return ( mSprops );
|
||||||
|
}
|
||||||
|
const double getFrameRate() const
|
||||||
|
{
|
||||||
|
return( mFrameRate );
|
||||||
|
}
|
||||||
|
void setFrameRate( double frameRate )
|
||||||
|
{
|
||||||
|
mFrameRate = frameRate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<MediaDescriptor *> MediaList;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static StaticPayloadDesc smStaticPayloads[];
|
||||||
|
static DynamicPayloadDesc smDynamicPayloads[];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string mUrl;
|
||||||
|
|
||||||
|
std::string mVersion;
|
||||||
|
std::string mOwner;
|
||||||
|
std::string mName;
|
||||||
|
std::string mInfo;
|
||||||
|
|
||||||
|
ConnInfo *mConnInfo;
|
||||||
|
BandInfo *mBandInfo;
|
||||||
|
std::string mTimeInfo;
|
||||||
|
StringVector mAttributes;
|
||||||
|
|
||||||
|
MediaList mMediaList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||||
~SessionDescriptor();
|
~SessionDescriptor();
|
||||||
|
|
||||||
const std::string &getUrl() const
|
const std::string &getUrl() const
|
||||||
{
|
{
|
||||||
return( mUrl );
|
return( mUrl );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNumStreams() const
|
int getNumStreams() const
|
||||||
{
|
{
|
||||||
return( mMediaList.size() );
|
return( mMediaList.size() );
|
||||||
}
|
}
|
||||||
MediaDescriptor *getStream( int index )
|
MediaDescriptor *getStream( int index )
|
||||||
{
|
{
|
||||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
return( mMediaList[index] );
|
return( mMediaList[index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *generateFormatContext() const;
|
AVFormatContext *generateFormatContext() const;
|
||||||
};
|
};
|
||||||
#if 0
|
#if 0
|
||||||
v=0
|
v=0
|
||||||
|
@ -254,7 +254,7 @@ a=mpeg4-esid:201
|
||||||
m=audio 0 RTP/AVP 0
|
m=audio 0 RTP/AVP 0
|
||||||
b=AS:64
|
b=AS:64
|
||||||
a=control:trackID=2
|
a=control:trackID=2
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ZM_SDP_H
|
#endif // ZM_SDP_H
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
#ifdef HAVE_SENDFILE4_SUPPORT
|
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = sendfile(out_fd, in_fd, offset, size);
|
err = sendfile(out_fd, in_fd, offset, size);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
#elif HAVE_SENDFILE7_SUPPORT
|
#elif HAVE_SENDFILE7_SUPPORT
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
||||||
int err;
|
int err;
|
||||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||||
if (err && errno != EAGAIN)
|
if (err && errno != EAGAIN)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
*offset += size;
|
*offset += size;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#error "Your platform does not support sendfile. Sorry."
|
#error "Your platform does not support sendfile. Sorry."
|
||||||
|
|
|
@ -32,326 +32,326 @@
|
||||||
StreamBase::~StreamBase()
|
StreamBase::~StreamBase()
|
||||||
{
|
{
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
if ( vid_stream )
|
if ( vid_stream )
|
||||||
{
|
{
|
||||||
delete vid_stream;
|
delete vid_stream;
|
||||||
vid_stream = NULL;
|
vid_stream = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
closeComms();
|
closeComms();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::loadMonitor( int monitor_id )
|
bool StreamBase::loadMonitor( int monitor_id )
|
||||||
{
|
{
|
||||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
monitor->connect();
|
monitor->connect();
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::checkInitialised()
|
bool StreamBase::checkInitialised()
|
||||||
{
|
{
|
||||||
if ( !monitor )
|
if ( !monitor )
|
||||||
{
|
{
|
||||||
Fatal( "Cannot stream, not initialised" );
|
Fatal( "Cannot stream, not initialised" );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::updateFrameRate( double fps )
|
void StreamBase::updateFrameRate( double fps )
|
||||||
{
|
{
|
||||||
base_fps = fps;
|
base_fps = fps;
|
||||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||||
frame_mod = 1;
|
frame_mod = 1;
|
||||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||||
// Min frame repeat?
|
// Min frame repeat?
|
||||||
while( effective_fps > maxfps )
|
while( effective_fps > maxfps )
|
||||||
{
|
{
|
||||||
effective_fps /= 2.0;
|
effective_fps /= 2.0;
|
||||||
frame_mod *= 2;
|
frame_mod *= 2;
|
||||||
}
|
}
|
||||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::checkCommandQueue()
|
bool StreamBase::checkCommandQueue()
|
||||||
{
|
{
|
||||||
if ( sd >= 0 )
|
if ( sd >= 0 )
|
||||||
|
{
|
||||||
|
CmdMsg msg;
|
||||||
|
memset( &msg, 0, sizeof(msg) );
|
||||||
|
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||||
|
if ( nbytes < 0 )
|
||||||
{
|
{
|
||||||
CmdMsg msg;
|
if ( errno != EAGAIN )
|
||||||
memset( &msg, 0, sizeof(msg) );
|
{
|
||||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||||
if ( nbytes < 0 )
|
}
|
||||||
{
|
|
||||||
if ( errno != EAGAIN )
|
|
||||||
{
|
|
||||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//else if ( (nbytes != sizeof(msg)) )
|
|
||||||
//{
|
|
||||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
|
||||||
//}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processCommand( &msg );
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return( false );
|
//else if ( (nbytes != sizeof(msg)) )
|
||||||
|
//{
|
||||||
|
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||||
|
//}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processCommand( &msg );
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *StreamBase::prepareImage( Image *image )
|
Image *StreamBase::prepareImage( Image *image )
|
||||||
{
|
{
|
||||||
static int last_scale = 0;
|
static int last_scale = 0;
|
||||||
static int last_zoom = 0;
|
static int last_zoom = 0;
|
||||||
static int last_x = 0;
|
static int last_x = 0;
|
||||||
static int last_y = 0;
|
static int last_y = 0;
|
||||||
|
|
||||||
if ( !last_scale )
|
if ( !last_scale )
|
||||||
last_scale = scale;
|
|
||||||
if ( !last_zoom )
|
|
||||||
last_zoom = zoom;
|
|
||||||
|
|
||||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
|
||||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
|
||||||
bool optimisedScaling = false;
|
|
||||||
|
|
||||||
bool image_copied = false;
|
|
||||||
|
|
||||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
|
||||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
|
||||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
|
||||||
|
|
||||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
|
||||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
|
||||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
|
||||||
|
|
||||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
|
||||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
|
||||||
|
|
||||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
|
||||||
|
|
||||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
|
||||||
|
|
||||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
|
||||||
|
|
||||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
|
||||||
|
|
||||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
|
||||||
|
|
||||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
|
||||||
|
|
||||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
|
||||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
|
||||||
|
|
||||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
|
||||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
|
||||||
|
|
||||||
if ( mag != ZM_SCALE_BASE )
|
|
||||||
{
|
|
||||||
if ( act_mag != ZM_SCALE_BASE )
|
|
||||||
{
|
|
||||||
Debug( 3, "Magnifying by %d", mag );
|
|
||||||
if ( !image_copied )
|
|
||||||
{
|
|
||||||
static Image copy_image;
|
|
||||||
copy_image.Assign( *image );
|
|
||||||
image = ©_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_scale = scale;
|
||||||
|
if ( !last_zoom )
|
||||||
last_zoom = zoom;
|
last_zoom = zoom;
|
||||||
last_x = x;
|
|
||||||
last_y = y;
|
|
||||||
|
|
||||||
return( image );
|
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||||
|
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||||
|
bool optimisedScaling = false;
|
||||||
|
|
||||||
|
bool image_copied = false;
|
||||||
|
|
||||||
|
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||||
|
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||||
|
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||||
|
|
||||||
|
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||||
|
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||||
|
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||||
|
|
||||||
|
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||||
|
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||||
|
|
||||||
|
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||||
|
|
||||||
|
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||||
|
|
||||||
|
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||||
|
|
||||||
|
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||||
|
|
||||||
|
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||||
|
|
||||||
|
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||||
|
|
||||||
|
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||||
|
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||||
|
|
||||||
|
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||||
|
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||||
|
|
||||||
|
if ( mag != ZM_SCALE_BASE )
|
||||||
|
{
|
||||||
|
if ( act_mag != ZM_SCALE_BASE )
|
||||||
|
{
|
||||||
|
Debug( 3, "Magnifying by %d", mag );
|
||||||
|
if ( !image_copied )
|
||||||
|
{
|
||||||
|
static Image copy_image;
|
||||||
|
copy_image.Assign( *image );
|
||||||
|
image = ©_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 )
|
bool StreamBase::sendTextFrame( const char *frame_text )
|
||||||
{
|
{
|
||||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||||
|
|
||||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||||
|
|
||||||
if ( scale != 100 )
|
if ( scale != 100 )
|
||||||
{
|
{
|
||||||
image.Scale( scale );
|
image.Scale( scale );
|
||||||
}
|
}
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
if ( type == STREAM_MPEG )
|
if ( type == STREAM_MPEG )
|
||||||
|
{
|
||||||
|
if ( !vid_stream )
|
||||||
{
|
{
|
||||||
if ( !vid_stream )
|
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||||
{
|
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
vid_stream->OpenStream();
|
||||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
|
||||||
vid_stream->OpenStream();
|
|
||||||
}
|
|
||||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
|
||||||
}
|
}
|
||||||
else
|
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||||
|
}
|
||||||
|
else
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
{
|
||||||
|
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
int n_bytes = 0;
|
||||||
|
|
||||||
|
image.EncodeJpeg( buffer, &n_bytes );
|
||||||
|
|
||||||
|
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||||
|
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||||
|
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||||
{
|
{
|
||||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||||
int n_bytes = 0;
|
return( false );
|
||||||
|
|
||||||
image.EncodeJpeg( buffer, &n_bytes );
|
|
||||||
|
|
||||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
|
||||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
|
||||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
|
||||||
{
|
|
||||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
fprintf( stdout, "\r\n\r\n" );
|
|
||||||
fflush( stdout );
|
|
||||||
}
|
}
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
return( true );
|
fflush( stdout );
|
||||||
|
}
|
||||||
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::openComms()
|
void StreamBase::openComms()
|
||||||
{
|
{
|
||||||
if ( connkey > 0 )
|
if ( connkey > 0 )
|
||||||
|
{
|
||||||
|
|
||||||
|
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||||
|
|
||||||
|
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||||
|
if ( lock_fd <= 0 )
|
||||||
{
|
{
|
||||||
|
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
lock_fd = 0;
|
||||||
|
|
||||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
|
||||||
if ( lock_fd <= 0 )
|
|
||||||
{
|
|
||||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
|
||||||
lock_fd = 0;
|
|
||||||
}
|
|
||||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
|
||||||
{
|
|
||||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
|
||||||
|
|
||||||
close(lock_fd);
|
|
||||||
lock_fd = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
|
||||||
if ( sd < 0 )
|
|
||||||
{
|
|
||||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
|
||||||
unlink( loc_sock_path );
|
|
||||||
|
|
||||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
|
||||||
loc_addr.sun_family = AF_UNIX;
|
|
||||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
|
||||||
{
|
|
||||||
Fatal( "Can't bind: %s", strerror(errno) );
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
|
||||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
|
||||||
rem_addr.sun_family = AF_UNIX;
|
|
||||||
}
|
}
|
||||||
|
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||||
|
{
|
||||||
|
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||||
|
|
||||||
|
close(lock_fd);
|
||||||
|
lock_fd = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||||
|
if ( sd < 0 )
|
||||||
|
{
|
||||||
|
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||||
|
unlink( loc_sock_path );
|
||||||
|
|
||||||
|
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||||
|
loc_addr.sun_family = AF_UNIX;
|
||||||
|
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||||
|
{
|
||||||
|
Fatal( "Can't bind: %s", strerror(errno) );
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||||
|
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||||
|
rem_addr.sun_family = AF_UNIX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::closeComms()
|
void StreamBase::closeComms()
|
||||||
{
|
{
|
||||||
if ( connkey > 0 )
|
if ( connkey > 0 )
|
||||||
|
{
|
||||||
|
if ( sd >= 0 )
|
||||||
{
|
{
|
||||||
if ( sd >= 0 )
|
close( sd );
|
||||||
{
|
sd = -1;
|
||||||
close( sd );
|
|
||||||
sd = -1;
|
|
||||||
}
|
|
||||||
if ( loc_sock_path[0] )
|
|
||||||
{
|
|
||||||
unlink( loc_sock_path );
|
|
||||||
}
|
|
||||||
if (lock_fd > 0)
|
|
||||||
{
|
|
||||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
|
||||||
unlink(sock_path_lock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if ( loc_sock_path[0] )
|
||||||
|
{
|
||||||
|
unlink( loc_sock_path );
|
||||||
|
}
|
||||||
|
if (lock_fd > 0)
|
||||||
|
{
|
||||||
|
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||||
|
unlink(sock_path_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
228
src/zm_stream.h
228
src/zm_stream.h
|
@ -33,149 +33,149 @@ class Monitor;
|
||||||
class StreamBase
|
class StreamBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||||
|
|
||||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||||
enum { DEFAULT_MAXFPS=10 };
|
enum { DEFAULT_MAXFPS=10 };
|
||||||
enum { DEFAULT_BITRATE=100000 };
|
enum { DEFAULT_BITRATE=100000 };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int msg_type;
|
int msg_type;
|
||||||
char msg_data[16];
|
char msg_data[16];
|
||||||
} CmdMsg;
|
} CmdMsg;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int msg_type;
|
int msg_type;
|
||||||
char msg_data[256];
|
char msg_data[256];
|
||||||
} DataMsg;
|
} DataMsg;
|
||||||
|
|
||||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Monitor *monitor;
|
Monitor *monitor;
|
||||||
|
|
||||||
StreamType type;
|
StreamType type;
|
||||||
const char *format;
|
const char *format;
|
||||||
int replay_rate;
|
int replay_rate;
|
||||||
int scale;
|
int scale;
|
||||||
int zoom;
|
int zoom;
|
||||||
double maxfps;
|
double maxfps;
|
||||||
int bitrate;
|
int bitrate;
|
||||||
unsigned short x, y;
|
unsigned short x, y;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int connkey;
|
int connkey;
|
||||||
int sd;
|
int sd;
|
||||||
char loc_sock_path[PATH_MAX];
|
char loc_sock_path[PATH_MAX];
|
||||||
struct sockaddr_un loc_addr;
|
struct sockaddr_un loc_addr;
|
||||||
char rem_sock_path[PATH_MAX];
|
char rem_sock_path[PATH_MAX];
|
||||||
struct sockaddr_un rem_addr;
|
struct sockaddr_un rem_addr;
|
||||||
char sock_path_lock[PATH_MAX];
|
char sock_path_lock[PATH_MAX];
|
||||||
int lock_fd;
|
int lock_fd;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool paused;
|
bool paused;
|
||||||
int step;
|
int step;
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
|
||||||
double base_fps;
|
double base_fps;
|
||||||
double effective_fps;
|
double effective_fps;
|
||||||
int frame_mod;
|
int frame_mod;
|
||||||
|
|
||||||
double last_frame_sent;
|
double last_frame_sent;
|
||||||
struct timeval last_frame_timestamp;
|
struct timeval last_frame_timestamp;
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
VideoStream *vid_stream;
|
VideoStream *vid_stream;
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
||||||
CmdMsg msg;
|
CmdMsg msg;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loadMonitor( int monitor_id );
|
bool loadMonitor( int monitor_id );
|
||||||
bool checkInitialised();
|
bool checkInitialised();
|
||||||
void updateFrameRate( double fps );
|
void updateFrameRate( double fps );
|
||||||
Image *prepareImage( Image *image );
|
Image *prepareImage( Image *image );
|
||||||
bool sendTextFrame( const char *text );
|
bool sendTextFrame( const char *text );
|
||||||
bool checkCommandQueue();
|
bool checkCommandQueue();
|
||||||
virtual void processCommand( const CmdMsg *msg )=0;
|
virtual void processCommand( const CmdMsg *msg )=0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamBase()
|
StreamBase()
|
||||||
{
|
{
|
||||||
monitor = 0;
|
monitor = 0;
|
||||||
|
|
||||||
type = DEFAULT_TYPE;
|
type = DEFAULT_TYPE;
|
||||||
format = "";
|
format = "";
|
||||||
replay_rate = DEFAULT_RATE;
|
replay_rate = DEFAULT_RATE;
|
||||||
scale = DEFAULT_SCALE;
|
scale = DEFAULT_SCALE;
|
||||||
zoom = DEFAULT_ZOOM;
|
zoom = DEFAULT_ZOOM;
|
||||||
maxfps = DEFAULT_MAXFPS;
|
maxfps = DEFAULT_MAXFPS;
|
||||||
bitrate = DEFAULT_BITRATE;
|
bitrate = DEFAULT_BITRATE;
|
||||||
|
|
||||||
paused = false;
|
paused = false;
|
||||||
step = 0;
|
step = 0;
|
||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
|
|
||||||
connkey = 0;
|
connkey = 0;
|
||||||
sd = -1;
|
sd = -1;
|
||||||
lock_fd = 0;
|
lock_fd = 0;
|
||||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||||
|
|
||||||
base_fps = 0.0;
|
base_fps = 0.0;
|
||||||
effective_fps = 0.0;
|
effective_fps = 0.0;
|
||||||
frame_mod = 1;
|
frame_mod = 1;
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
vid_stream = 0;
|
vid_stream = 0;
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
}
|
}
|
||||||
virtual ~StreamBase();
|
virtual ~StreamBase();
|
||||||
|
|
||||||
void setStreamType( StreamType p_type )
|
void setStreamType( StreamType p_type )
|
||||||
{
|
{
|
||||||
type = p_type;
|
type = p_type;
|
||||||
}
|
}
|
||||||
void setStreamFormat( const char *p_format )
|
void setStreamFormat( const char *p_format )
|
||||||
{
|
{
|
||||||
format = p_format;
|
format = p_format;
|
||||||
}
|
}
|
||||||
void setStreamScale( int p_scale )
|
void setStreamScale( int p_scale )
|
||||||
{
|
{
|
||||||
scale = p_scale;
|
scale = p_scale;
|
||||||
}
|
}
|
||||||
void setStreamReplayRate( int p_rate )
|
void setStreamReplayRate( int p_rate )
|
||||||
{
|
{
|
||||||
replay_rate = p_rate;
|
replay_rate = p_rate;
|
||||||
}
|
}
|
||||||
void setStreamMaxFPS( double p_maxfps )
|
void setStreamMaxFPS( double p_maxfps )
|
||||||
{
|
{
|
||||||
maxfps = p_maxfps;
|
maxfps = p_maxfps;
|
||||||
}
|
}
|
||||||
void setStreamBitrate( int p_bitrate )
|
void setStreamBitrate( int p_bitrate )
|
||||||
{
|
{
|
||||||
bitrate = p_bitrate;
|
bitrate = p_bitrate;
|
||||||
}
|
}
|
||||||
void setStreamQueue( int p_connkey )
|
void setStreamQueue( int p_connkey )
|
||||||
{
|
{
|
||||||
connkey = p_connkey;
|
connkey = p_connkey;
|
||||||
}
|
}
|
||||||
virtual void openComms();
|
virtual void openComms();
|
||||||
virtual void closeComms();
|
virtual void closeComms();
|
||||||
virtual void runStream()=0;
|
virtual void runStream()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_STREAM_H
|
#endif // ZM_STREAM_H
|
||||||
|
|
|
@ -29,310 +29,310 @@
|
||||||
|
|
||||||
struct timespec getTimeout( int secs )
|
struct timespec getTimeout( int secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout;
|
struct timespec timeout;
|
||||||
struct timeval temp_timeout;
|
struct timeval temp_timeout;
|
||||||
gettimeofday( &temp_timeout, 0 );
|
gettimeofday( &temp_timeout, 0 );
|
||||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||||
return( timeout );
|
return( timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec getTimeout( double secs )
|
struct timespec getTimeout( double secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout;
|
struct timespec timeout;
|
||||||
struct timeval temp_timeout;
|
struct timeval temp_timeout;
|
||||||
gettimeofday( &temp_timeout, 0 );
|
gettimeofday( &temp_timeout, 0 );
|
||||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||||
if ( timeout.tv_nsec > 1000000000 )
|
if ( timeout.tv_nsec > 1000000000 )
|
||||||
{
|
{
|
||||||
timeout.tv_sec += 1;
|
timeout.tv_sec += 1;
|
||||||
timeout.tv_nsec -= 1000000000;
|
timeout.tv_nsec -= 1000000000;
|
||||||
}
|
}
|
||||||
return( timeout );
|
return( timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Mutex()
|
Mutex::Mutex()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::~Mutex()
|
Mutex::~Mutex()
|
||||||
{
|
{
|
||||||
if ( locked() )
|
if ( locked() )
|
||||||
Warning( "Destroying mutex when locked" );
|
Warning( "Destroying mutex when locked" );
|
||||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock()
|
void Mutex::lock()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock( int secs )
|
void Mutex::lock( int secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock( double secs )
|
void Mutex::lock( double secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::unlock()
|
void Mutex::unlock()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mutex::locked()
|
bool Mutex::locked()
|
||||||
{
|
{
|
||||||
int state = pthread_mutex_trylock( &mMutex );
|
int state = pthread_mutex_trylock( &mMutex );
|
||||||
if ( state != 0 && state != EBUSY )
|
if ( state != 0 && state != EBUSY )
|
||||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||||
if ( state != EBUSY )
|
if ( state != EBUSY )
|
||||||
unlock();
|
unlock();
|
||||||
return( state == EBUSY );
|
return( state == EBUSY );
|
||||||
}
|
}
|
||||||
|
|
||||||
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
||||||
{
|
{
|
||||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Condition::~Condition()
|
Condition::~Condition()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::wait()
|
void Condition::wait()
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( int secs )
|
bool Condition::wait( int secs )
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
Debug( 8, "Waiting for %d seconds", secs );
|
Debug( 8, "Waiting for %d seconds", secs );
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||||
return( errno != ETIMEDOUT );
|
return( errno != ETIMEDOUT );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( double secs )
|
bool Condition::wait( double secs )
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||||
return( errno != ETIMEDOUT );
|
return( errno != ETIMEDOUT );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::signal()
|
void Condition::signal()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::broadcast()
|
void Condition::broadcast()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getValue() const
|
template <class T> const T ThreadData<T>::getValue() const
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> T ThreadData<T>::setValue( const T value )
|
template <class T> T ThreadData<T>::setValue( const T value )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
const T valueCopy = mValue = value;
|
const T valueCopy = mValue = value;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %p", this );
|
Debug( 8, "Waiting for value update, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait();
|
mCondition.wait();
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait( secs );
|
mCondition.wait( secs );
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait( secs );
|
mCondition.wait( secs );
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
||||||
{
|
{
|
||||||
Debug( 8, "Updating value with signal, %p", this );
|
Debug( 8, "Updating value with signal, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mValue = value;
|
mValue = value;
|
||||||
mChanged = true;
|
mChanged = true;
|
||||||
mCondition.signal();
|
mCondition.signal();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Updated value, %p", this );
|
Debug( 9, "Updated value, %p", this );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
||||||
{
|
{
|
||||||
Debug( 8, "Updating value with broadcast, %p", this );
|
Debug( 8, "Updating value with broadcast, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mValue = value;
|
mValue = value;
|
||||||
mChanged = true;
|
mChanged = true;
|
||||||
mCondition.broadcast();
|
mCondition.broadcast();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Updated value, %p", this );
|
Debug( 9, "Updated value, %p", this );
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::Thread() :
|
Thread::Thread() :
|
||||||
mThreadCondition( mThreadMutex ),
|
mThreadCondition( mThreadMutex ),
|
||||||
mPid( -1 ),
|
mPid( -1 ),
|
||||||
mStarted( false ),
|
mStarted( false ),
|
||||||
mRunning( false )
|
mRunning( false )
|
||||||
{
|
{
|
||||||
Debug( 1, "Creating thread" );
|
Debug( 1, "Creating thread" );
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::~Thread()
|
Thread::~Thread()
|
||||||
{
|
{
|
||||||
Debug( 1, "Destroying thread %d", mPid );
|
Debug( 1, "Destroying thread %d", mPid );
|
||||||
if ( mStarted )
|
if ( mStarted )
|
||||||
join();
|
join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *Thread::mThreadFunc( void *arg )
|
void *Thread::mThreadFunc( void *arg )
|
||||||
{
|
{
|
||||||
Debug( 2, "Invoking thread" );
|
Debug( 2, "Invoking thread" );
|
||||||
|
|
||||||
Thread *thisPtr = (Thread *)arg;
|
Thread *thisPtr = (Thread *)arg;
|
||||||
thisPtr->status = 0;
|
thisPtr->status = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
thisPtr->mThreadMutex.lock();
|
thisPtr->mThreadMutex.lock();
|
||||||
thisPtr->mPid = thisPtr->id();
|
thisPtr->mPid = thisPtr->id();
|
||||||
thisPtr->mThreadCondition.signal();
|
thisPtr->mThreadCondition.signal();
|
||||||
thisPtr->mThreadMutex.unlock();
|
thisPtr->mThreadMutex.unlock();
|
||||||
thisPtr->mRunning = true;
|
thisPtr->mRunning = true;
|
||||||
thisPtr->status = thisPtr->run();
|
thisPtr->status = thisPtr->run();
|
||||||
thisPtr->mRunning = false;
|
thisPtr->mRunning = false;
|
||||||
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
||||||
return (void *)&(thisPtr->status);
|
return (void *)&(thisPtr->status);
|
||||||
}
|
}
|
||||||
catch ( const ThreadException &e )
|
catch ( const ThreadException &e )
|
||||||
{
|
{
|
||||||
Error( "%s", e.getMessage().c_str() );
|
Error( "%s", e.getMessage().c_str() );
|
||||||
thisPtr->mRunning = false;
|
thisPtr->mRunning = false;
|
||||||
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
||||||
return (void *)-1;
|
return (void *)-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::start()
|
void Thread::start()
|
||||||
{
|
{
|
||||||
Debug( 1, "Starting thread" );
|
Debug( 1, "Starting thread" );
|
||||||
if ( isThread() )
|
if ( isThread() )
|
||||||
throw ThreadException( "Can't self start thread" );
|
throw ThreadException( "Can't self start thread" );
|
||||||
mThreadMutex.lock();
|
mThreadMutex.lock();
|
||||||
if ( !mStarted )
|
if ( !mStarted )
|
||||||
{
|
{
|
||||||
pthread_attr_t threadAttrs;
|
pthread_attr_t threadAttrs;
|
||||||
pthread_attr_init( &threadAttrs );
|
pthread_attr_init( &threadAttrs );
|
||||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||||
|
|
||||||
mStarted = true;
|
mStarted = true;
|
||||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||||
pthread_attr_destroy( &threadAttrs );
|
pthread_attr_destroy( &threadAttrs );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error( "Attempt to start already running thread %d", mPid );
|
Error( "Attempt to start already running thread %d", mPid );
|
||||||
}
|
}
|
||||||
mThreadCondition.wait();
|
mThreadCondition.wait();
|
||||||
mThreadMutex.unlock();
|
mThreadMutex.unlock();
|
||||||
Debug( 1, "Started thread %d", mPid );
|
Debug( 1, "Started thread %d", mPid );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::join()
|
void Thread::join()
|
||||||
{
|
{
|
||||||
Debug( 1, "Joining thread %d", mPid );
|
Debug( 1, "Joining thread %d", mPid );
|
||||||
if ( isThread() )
|
if ( isThread() )
|
||||||
throw ThreadException( "Can't self join thread" );
|
throw ThreadException( "Can't self join thread" );
|
||||||
mThreadMutex.lock();
|
mThreadMutex.lock();
|
||||||
if ( mPid >= 0 )
|
if ( mPid >= 0 )
|
||||||
|
{
|
||||||
|
if ( mStarted )
|
||||||
{
|
{
|
||||||
if ( mStarted )
|
void *threadStatus = 0;
|
||||||
{
|
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||||
void *threadStatus = 0;
|
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
mStarted = false;
|
||||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||||
mStarted = false;
|
|
||||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Warning( "Attempt to join already finished thread %d", mPid );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Warning( "Attempt to join non-started thread %d", mPid );
|
Warning( "Attempt to join already finished thread %d", mPid );
|
||||||
}
|
}
|
||||||
mThreadMutex.unlock();
|
}
|
||||||
Debug( 1, "Joined thread %d", mPid );
|
else
|
||||||
|
{
|
||||||
|
Warning( "Attempt to join non-started thread %d", mPid );
|
||||||
|
}
|
||||||
|
mThreadMutex.unlock();
|
||||||
|
Debug( 1, "Joined thread %d", mPid );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::kill( int signal )
|
void Thread::kill( int signal )
|
||||||
{
|
{
|
||||||
pthread_kill( mThread, signal );
|
pthread_kill( mThread, signal );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some explicit template instantiations
|
// Some explicit template instantiations
|
||||||
|
|
308
src/zm_thread.h
308
src/zm_thread.h
|
@ -36,27 +36,27 @@ class ThreadException : public Exception
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t pid() {
|
pid_t pid() {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
# else
|
# else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t pid() { return( pthread_self() ); }
|
pthread_t pid() { return( pthread_self() ); }
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mutex
|
class Mutex
|
||||||
|
@ -64,215 +64,215 @@ class Mutex
|
||||||
friend class Condition;
|
friend class Condition;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t mMutex;
|
pthread_mutex_t mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Mutex();
|
Mutex();
|
||||||
~Mutex();
|
~Mutex();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t *getMutex()
|
pthread_mutex_t *getMutex()
|
||||||
{
|
{
|
||||||
return( &mMutex );
|
return( &mMutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void lock();
|
void lock();
|
||||||
void lock( int secs );
|
void lock( int secs );
|
||||||
void lock( double secs );
|
void lock( double secs );
|
||||||
void unlock();
|
void unlock();
|
||||||
bool locked();
|
bool locked();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScopedMutex
|
class ScopedMutex
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex &mMutex;
|
Mutex &mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
}
|
}
|
||||||
~ScopedMutex()
|
~ScopedMutex()
|
||||||
{
|
{
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScopedMutex( const ScopedMutex & );
|
ScopedMutex( const ScopedMutex & );
|
||||||
};
|
};
|
||||||
|
|
||||||
class Condition
|
class Condition
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex &mMutex;
|
Mutex &mMutex;
|
||||||
pthread_cond_t mCondition;
|
pthread_cond_t mCondition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Condition( Mutex &mutex );
|
Condition( Mutex &mutex );
|
||||||
~Condition();
|
~Condition();
|
||||||
|
|
||||||
void wait();
|
void wait();
|
||||||
bool wait( int secs );
|
bool wait( int secs );
|
||||||
bool wait( double secs );
|
bool wait( double secs );
|
||||||
void signal();
|
void signal();
|
||||||
void broadcast();
|
void broadcast();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Semaphore : public Condition
|
class Semaphore : public Condition
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex mMutex;
|
Mutex mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Semaphore() : Condition( mMutex )
|
Semaphore() : Condition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait()
|
void wait()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::wait();
|
Condition::wait();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
bool wait( int secs )
|
bool wait( int secs )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
bool result = Condition::wait( secs );
|
bool result = Condition::wait( secs );
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
bool wait( double secs )
|
bool wait( double secs )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
bool result = Condition::wait( secs );
|
bool result = Condition::wait( secs );
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
void signal()
|
void signal()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::signal();
|
Condition::signal();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
void broadcast()
|
void broadcast()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::broadcast();
|
Condition::broadcast();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> class ThreadData
|
template <class T> class ThreadData
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
T mValue;
|
T mValue;
|
||||||
mutable bool mChanged;
|
mutable bool mChanged;
|
||||||
mutable Mutex mMutex;
|
mutable Mutex mMutex;
|
||||||
mutable Condition mCondition;
|
mutable Condition mCondition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
//~ThreadData() {}
|
//~ThreadData() {}
|
||||||
|
|
||||||
__attribute__((used)) operator T() const
|
__attribute__((used)) operator T() const
|
||||||
{
|
{
|
||||||
return( getValue() );
|
return( getValue() );
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T operator=( const T value )
|
__attribute__((used)) const T operator=( const T value )
|
||||||
{
|
{
|
||||||
return( setValue( value ) );
|
return( setValue( value ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((used)) const T getValueImmediate() const
|
__attribute__((used)) const T getValueImmediate() const
|
||||||
{
|
{
|
||||||
return( mValue );
|
return( mValue );
|
||||||
}
|
}
|
||||||
__attribute__((used)) T setValueImmediate( const T value )
|
__attribute__((used)) T setValueImmediate( const T value )
|
||||||
{
|
{
|
||||||
return( mValue = value );
|
return( mValue = value );
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T getValue() const;
|
__attribute__((used)) const T getValue() const;
|
||||||
__attribute__((used)) T setValue( const T value );
|
__attribute__((used)) T setValue( const T value );
|
||||||
__attribute__((used)) const T getUpdatedValue() const;
|
__attribute__((used)) const T getUpdatedValue() const;
|
||||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||||
__attribute__((used)) void updateValueSignal( const T value );
|
__attribute__((used)) void updateValueSignal( const T value );
|
||||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thread
|
class Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef void *(*ThreadFunc)( void * );
|
typedef void *(*ThreadFunc)( void * );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
pthread_t mThread;
|
pthread_t mThread;
|
||||||
|
|
||||||
Mutex mThreadMutex;
|
Mutex mThreadMutex;
|
||||||
Condition mThreadCondition;
|
Condition mThreadCondition;
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t mPid;
|
pid_t mPid;
|
||||||
#else
|
#else
|
||||||
pthread_t mPid;
|
pthread_t mPid;
|
||||||
#endif
|
#endif
|
||||||
bool mStarted;
|
bool mStarted;
|
||||||
bool mRunning;
|
bool mRunning;
|
||||||
int status; // Used in various funcions to get around return a local variable
|
int status; // Used in various funcions to get around return a local variable
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Thread();
|
Thread();
|
||||||
virtual ~Thread();
|
virtual ~Thread();
|
||||||
|
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t id() const
|
pid_t id() const
|
||||||
{
|
{
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
|
|
||||||
#else
|
#else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t id() const
|
pthread_t id() const
|
||||||
{
|
{
|
||||||
return( pthread_self() );
|
return( pthread_self() );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void exit( int p_status = 0 )
|
void exit( int p_status = 0 )
|
||||||
{
|
{
|
||||||
//INFO( "Exiting" );
|
//INFO( "Exiting" );
|
||||||
pthread_exit( (void *)&p_status );
|
pthread_exit( (void *)&p_status );
|
||||||
}
|
}
|
||||||
static void *mThreadFunc( void *arg );
|
static void *mThreadFunc( void *arg );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual int run() = 0;
|
virtual int run() = 0;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void join();
|
void join();
|
||||||
void kill( int signal );
|
void kill( int signal );
|
||||||
bool isThread()
|
bool isThread()
|
||||||
{
|
{
|
||||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||||
}
|
}
|
||||||
bool isStarted() const { return( mStarted ); }
|
bool isStarted() const { return( mStarted ); }
|
||||||
bool isRunning() const { return( mRunning ); }
|
bool isRunning() const { return( mRunning ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_THREAD_H
|
#endif // ZM_THREAD_H
|
||||||
|
|
188
src/zm_time.h
188
src/zm_time.h
|
@ -29,48 +29,48 @@
|
||||||
|
|
||||||
struct DeltaTimeval
|
struct DeltaTimeval
|
||||||
{
|
{
|
||||||
bool positive;
|
bool positive;
|
||||||
unsigned long delta;
|
unsigned long delta;
|
||||||
unsigned long sec;
|
unsigned long sec;
|
||||||
unsigned long fsec;
|
unsigned long fsec;
|
||||||
unsigned long prec;
|
unsigned long prec;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DT_GRAN_1000000 1000000
|
#define DT_GRAN_1000000 1000000
|
||||||
#define DT_PREC_6 DT_GRAN_1000000
|
#define DT_PREC_6 DT_GRAN_1000000
|
||||||
#define DT_GRAN_100000 100000
|
#define DT_GRAN_100000 100000
|
||||||
#define DT_PREC_5 DT_GRAN_100000
|
#define DT_PREC_5 DT_GRAN_100000
|
||||||
#define DT_GRAN_10000 10000
|
#define DT_GRAN_10000 10000
|
||||||
#define DT_PREC_4 DT_GRAN_10000
|
#define DT_PREC_4 DT_GRAN_10000
|
||||||
#define DT_GRAN_1000 1000
|
#define DT_GRAN_1000 1000
|
||||||
#define DT_PREC_3 DT_GRAN_1000
|
#define DT_PREC_3 DT_GRAN_1000
|
||||||
#define DT_GRAN_100 100
|
#define DT_GRAN_100 100
|
||||||
#define DT_PREC_2 DT_GRAN_100
|
#define DT_PREC_2 DT_GRAN_100
|
||||||
#define DT_GRAN_10 10
|
#define DT_GRAN_10 10
|
||||||
#define DT_PREC_1 DT_GRAN_10
|
#define DT_PREC_1 DT_GRAN_10
|
||||||
|
|
||||||
#define DT_MAXGRAN DT_GRAN_1000000
|
#define DT_MAXGRAN DT_GRAN_1000000
|
||||||
|
|
||||||
// This obviously wouldn't work for massive deltas but as it's mostly
|
// This obviously wouldn't work for massive deltas but as it's mostly
|
||||||
// for frames it will only usually be a fraction of a second or so
|
// for frames it will only usually be a fraction of a second or so
|
||||||
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
||||||
{ \
|
{ \
|
||||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||||
result.positive = (delta>=0); \
|
result.positive = (delta>=0); \
|
||||||
result.delta = abs(delta); \
|
result.delta = abs(delta); \
|
||||||
result.sec = result.delta/(precision); \
|
result.sec = result.delta/(precision); \
|
||||||
result.fsec = result.delta%(precision); \
|
result.fsec = result.delta%(precision); \
|
||||||
result.prec = (precision); \
|
result.prec = (precision); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
||||||
{ \
|
{ \
|
||||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||||
result.positive = (delta>=0); \
|
result.positive = (delta>=0); \
|
||||||
result.delta = abs(delta); \
|
result.delta = abs(delta); \
|
||||||
result.sec = result.delta/(precision); \
|
result.sec = result.delta/(precision); \
|
||||||
result.fsec = result.delta%(precision); \
|
result.fsec = result.delta%(precision); \
|
||||||
result.prec = (precision); \
|
result.prec = (precision); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USEC_PER_SEC 1000000
|
#define USEC_PER_SEC 1000000
|
||||||
|
@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t;
|
||||||
|
|
||||||
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffUsec( struct timeval first )
|
inline int tvDiffUsec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffUsec( first, now ) );
|
return( tvDiffUsec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffMsec( struct timeval first )
|
inline int tvDiffMsec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffMsec( first, now ) );
|
return( tvDiffMsec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double tvDiffSec( struct timeval first, struct timeval last )
|
inline double tvDiffSec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double tvDiffSec( struct timeval first )
|
inline double tvDiffSec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffSec( first, now ) );
|
return( tvDiffSec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvZero()
|
inline struct timeval tvZero()
|
||||||
{
|
{
|
||||||
struct timeval t = { 0, 0 };
|
struct timeval t = { 0, 0 };
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvIsZero( const struct timeval t )
|
inline int tvIsZero( const struct timeval t )
|
||||||
{
|
{
|
||||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
if ( t1.tv_sec < t2.tv_sec )
|
if ( t1.tv_sec < t2.tv_sec )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if ( t1.tv_sec > t2.tv_sec )
|
if ( t1.tv_sec > t2.tv_sec )
|
||||||
return( 1 );
|
return( 1 );
|
||||||
if ( t1.tv_usec < t2.tv_usec )
|
if ( t1.tv_usec < t2.tv_usec )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if ( t1.tv_usec > t2.tv_usec )
|
if ( t1.tv_usec > t2.tv_usec )
|
||||||
return( 1 );
|
return( 1 );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvEq( struct timeval t1, struct timeval t2 )
|
inline int tvEq( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvNow( void )
|
inline struct timeval tvNow( void )
|
||||||
{
|
{
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
gettimeofday( &t, NULL );
|
gettimeofday( &t, NULL );
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvCheck( struct timeval &t )
|
inline struct timeval tvCheck( struct timeval &t )
|
||||||
{
|
{
|
||||||
if ( t.tv_usec >= USEC_PER_SEC )
|
if ( t.tv_usec >= USEC_PER_SEC )
|
||||||
{
|
{
|
||||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||||
t.tv_usec %= USEC_PER_SEC;
|
t.tv_usec %= USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
else if ( t.tv_usec < 0 )
|
else if ( t.tv_usec < 0 )
|
||||||
{
|
{
|
||||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||||
t.tv_usec = 0;
|
t.tv_usec = 0;
|
||||||
}
|
}
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add t2 to t1
|
// Add t2 to t1
|
||||||
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
tvCheck(t1);
|
tvCheck(t1);
|
||||||
tvCheck(t2);
|
tvCheck(t2);
|
||||||
t1.tv_sec += t2.tv_sec;
|
t1.tv_sec += t2.tv_sec;
|
||||||
t1.tv_usec += t2.tv_usec;
|
t1.tv_usec += t2.tv_usec;
|
||||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||||
{
|
{
|
||||||
t1.tv_sec++;
|
t1.tv_sec++;
|
||||||
t1.tv_usec -= USEC_PER_SEC;
|
t1.tv_usec -= USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
return( t1 );
|
return( t1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract t2 from t1
|
// Subtract t2 from t1
|
||||||
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
tvCheck(t1);
|
tvCheck(t1);
|
||||||
tvCheck(t2);
|
tvCheck(t2);
|
||||||
t1.tv_sec -= t2.tv_sec;
|
t1.tv_sec -= t2.tv_sec;
|
||||||
t1.tv_usec -= t2.tv_usec;
|
t1.tv_usec -= t2.tv_usec;
|
||||||
if ( t1.tv_usec < 0 )
|
if ( t1.tv_usec < 0 )
|
||||||
{
|
{
|
||||||
t1.tv_sec--;
|
t1.tv_sec--;
|
||||||
t1.tv_usec += USEC_PER_SEC;
|
t1.tv_usec += USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
return( t1 ) ;
|
return( t1 ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
||||||
{
|
{
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
t.tv_sec = sec;
|
t.tv_sec = sec;
|
||||||
t.tv_usec = usec;
|
t.tv_usec = usec;
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_TIME_H
|
#endif // ZM_TIME_H
|
||||||
|
|
114
src/zm_timer.cpp
114
src/zm_timer.cpp
|
@ -24,96 +24,96 @@
|
||||||
int Timer::TimerThread::mNextTimerId = 0;
|
int Timer::TimerThread::mNextTimerId = 0;
|
||||||
|
|
||||||
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
||||||
mTimerId( 0 ),
|
mTimerId( 0 ),
|
||||||
mTimer( timer ),
|
mTimer( timer ),
|
||||||
mDuration( duration ),
|
mDuration( duration ),
|
||||||
mRepeat( repeat ),
|
mRepeat( repeat ),
|
||||||
mReset( false ),
|
mReset( false ),
|
||||||
mExpiryFlag( true )
|
mExpiryFlag( true )
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
mTimerId = mNextTimerId++;
|
mTimerId = mNextTimerId++;
|
||||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::TimerThread::~TimerThread()
|
Timer::TimerThread::~TimerThread()
|
||||||
{
|
{
|
||||||
cancel();
|
cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::TimerThread::cancel()
|
void Timer::TimerThread::cancel()
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
if ( mRunning )
|
if ( mRunning )
|
||||||
{
|
{
|
||||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||||
mRepeat = false;
|
mRepeat = false;
|
||||||
mReset = false;
|
mReset = false;
|
||||||
mExpiryFlag.updateValueSignal( false );
|
mExpiryFlag.updateValueSignal( false );
|
||||||
}
|
}
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::TimerThread::reset()
|
void Timer::TimerThread::reset()
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
if ( mRunning )
|
if ( mRunning )
|
||||||
{
|
{
|
||||||
Debug( 4, "Resetting timer" );
|
Debug( 4, "Resetting timer" );
|
||||||
mReset = true;
|
mReset = true;
|
||||||
mExpiryFlag.updateValueSignal( false );
|
mExpiryFlag.updateValueSignal( false );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||||
}
|
}
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Timer::TimerThread::run()
|
int Timer::TimerThread::run()
|
||||||
{
|
{
|
||||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||||
bool timerExpired = false;
|
bool timerExpired = false;
|
||||||
do
|
do
|
||||||
|
{
|
||||||
|
mAccessMutex.lock();
|
||||||
|
mReset = false;
|
||||||
|
mExpiryFlag.setValue( true );
|
||||||
|
mAccessMutex.unlock();
|
||||||
|
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||||
|
mAccessMutex.lock();
|
||||||
|
if ( timerExpired )
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
Debug( 4, "Timer %d expired", mTimerId );
|
||||||
mReset = false;
|
mTimer.expire();
|
||||||
mExpiryFlag.setValue( true );
|
}
|
||||||
mAccessMutex.unlock();
|
else
|
||||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
{
|
||||||
mAccessMutex.lock();
|
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||||
if ( timerExpired )
|
}
|
||||||
{
|
mAccessMutex.unlock();
|
||||||
Debug( 4, "Timer %d expired", mTimerId );
|
} while ( mRepeat || (mReset && !timerExpired) );
|
||||||
mTimer.expire();
|
return( timerExpired );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
|
||||||
}
|
|
||||||
mAccessMutex.unlock();
|
|
||||||
} while ( mRepeat || (mReset && !timerExpired) );
|
|
||||||
return( timerExpired );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
||||||
{
|
{
|
||||||
mTimerThread.start();
|
mTimerThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::~Timer()
|
Timer::~Timer()
|
||||||
{
|
{
|
||||||
//cancel();
|
//cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Timer::cancel()
|
void Timer::Timer::cancel()
|
||||||
{
|
{
|
||||||
mTimerThread.cancel();
|
mTimerThread.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Timer::reset()
|
void Timer::Timer::reset()
|
||||||
{
|
{
|
||||||
mTimerThread.reset();
|
mTimerThread.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
120
src/zm_timer.h
120
src/zm_timer.h
|
@ -30,81 +30,81 @@
|
||||||
class Timer
|
class Timer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
class TimerException : public Exception
|
class TimerException : public Exception
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t pid() {
|
pid_t pid() {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
#else
|
#else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t pid() { return( pthread_self() ); }
|
pthread_t pid() { return( pthread_self() ); }
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TimerThread : public Thread
|
|
||||||
{
|
{
|
||||||
private:
|
}
|
||||||
typedef ThreadData<bool> ExpiryFlag;
|
};
|
||||||
|
|
||||||
private:
|
class TimerThread : public Thread
|
||||||
static int mNextTimerId;
|
{
|
||||||
|
private:
|
||||||
|
typedef ThreadData<bool> ExpiryFlag;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mTimerId;
|
static int mNextTimerId;
|
||||||
Timer &mTimer;
|
|
||||||
int mDuration;
|
|
||||||
int mRepeat;
|
|
||||||
int mReset;
|
|
||||||
ExpiryFlag mExpiryFlag;
|
|
||||||
Mutex mAccessMutex;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void quit()
|
int mTimerId;
|
||||||
{
|
Timer &mTimer;
|
||||||
cancel();
|
int mDuration;
|
||||||
}
|
int mRepeat;
|
||||||
|
int mReset;
|
||||||
|
ExpiryFlag mExpiryFlag;
|
||||||
|
Mutex mAccessMutex;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
void quit()
|
||||||
~TimerThread();
|
{
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
|
||||||
void cancel();
|
public:
|
||||||
void reset();
|
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||||
int run();
|
~TimerThread();
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TimerThread mTimerThread;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Timer( int timeout, bool repeat=false );
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~Timer();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void expire()=0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void cancel();
|
void cancel();
|
||||||
void reset();
|
void reset();
|
||||||
|
int run();
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TimerThread mTimerThread;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Timer( int timeout, bool repeat=false );
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Timer();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void expire()=0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void cancel();
|
||||||
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_TIMER_H
|
#endif // ZM_TIMER_H
|
||||||
|
|
332
src/zm_user.cpp
332
src/zm_user.cpp
|
@ -29,119 +29,119 @@
|
||||||
|
|
||||||
User::User()
|
User::User()
|
||||||
{
|
{
|
||||||
username[0] = password[0] = 0;
|
username[0] = password[0] = 0;
|
||||||
enabled = false;
|
enabled = false;
|
||||||
stream = events = control = monitors = system = PERM_NONE;
|
stream = events = control = monitors = system = PERM_NONE;
|
||||||
monitor_ids = 0;
|
monitor_ids = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
User::User( MYSQL_ROW &dbrow )
|
User::User( MYSQL_ROW &dbrow )
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
strncpy( username, dbrow[index++], sizeof(username) );
|
strncpy( username, dbrow[index++], sizeof(username) );
|
||||||
strncpy( password, dbrow[index++], sizeof(password) );
|
strncpy( password, dbrow[index++], sizeof(password) );
|
||||||
enabled = (bool)atoi( dbrow[index++] );
|
enabled = (bool)atoi( dbrow[index++] );
|
||||||
stream = (Permission)atoi( dbrow[index++] );
|
stream = (Permission)atoi( dbrow[index++] );
|
||||||
events = (Permission)atoi( dbrow[index++] );
|
events = (Permission)atoi( dbrow[index++] );
|
||||||
control = (Permission)atoi( dbrow[index++] );
|
control = (Permission)atoi( dbrow[index++] );
|
||||||
monitors = (Permission)atoi( dbrow[index++] );
|
monitors = (Permission)atoi( dbrow[index++] );
|
||||||
system = (Permission)atoi( dbrow[index++] );
|
system = (Permission)atoi( dbrow[index++] );
|
||||||
monitor_ids = 0;
|
monitor_ids = 0;
|
||||||
char *monitor_ids_str = dbrow[index++];
|
char *monitor_ids_str = dbrow[index++];
|
||||||
if ( monitor_ids_str && *monitor_ids_str )
|
if ( monitor_ids_str && *monitor_ids_str )
|
||||||
{
|
{
|
||||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||||
int n_monitor_ids = 0;
|
int n_monitor_ids = 0;
|
||||||
const char *ptr = monitor_ids_str;
|
const char *ptr = monitor_ids_str;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
int id = 0;
|
int id = 0;
|
||||||
while( isdigit( *ptr ) )
|
while( isdigit( *ptr ) )
|
||||||
{
|
{
|
||||||
id *= 10;
|
id *= 10;
|
||||||
id += *ptr-'0';
|
id += *ptr-'0';
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
if ( id )
|
if ( id )
|
||||||
{
|
{
|
||||||
monitor_ids[n_monitor_ids++] = id;
|
monitor_ids[n_monitor_ids++] = id;
|
||||||
if ( !*ptr )
|
if ( !*ptr )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while ( !isdigit( *ptr ) )
|
while ( !isdigit( *ptr ) )
|
||||||
ptr++;
|
ptr++;
|
||||||
} while( *ptr );
|
} while( *ptr );
|
||||||
monitor_ids[n_monitor_ids] = 0;
|
monitor_ids[n_monitor_ids] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User::~User()
|
User::~User()
|
||||||
{
|
{
|
||||||
delete monitor_ids;
|
delete monitor_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool User::canAccess( int monitor_id )
|
bool User::canAccess( int monitor_id )
|
||||||
{
|
{
|
||||||
if ( !monitor_ids )
|
if ( !monitor_ids )
|
||||||
{
|
{
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
for ( int i = 0; monitor_ids[i]; i++ )
|
for ( int i = 0; monitor_ids[i]; i++ )
|
||||||
{
|
{
|
||||||
if ( monitor_ids[i] == monitor_id )
|
if ( monitor_ids[i] == monitor_id )
|
||||||
{
|
{
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to load a user from username and password
|
// Function to load a user from username and password
|
||||||
// Please note that in auth relay mode = none, password is NULL
|
// Please note that in auth relay mode = none, password is NULL
|
||||||
User *zmLoadUser( const char *username, const char *password )
|
User *zmLoadUser( const char *username, const char *password )
|
||||||
{
|
{
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
char safer_username[65]; // current db username size is 32
|
char safer_username[65]; // current db username size is 32
|
||||||
char safer_password[129]; // current db password size is 64
|
char safer_password[129]; // current db password size is 64
|
||||||
|
|
||||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||||
|
|
||||||
if ( password ) {
|
if ( password ) {
|
||||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||||
} else {
|
} else {
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result )
|
if ( !result )
|
||||||
{
|
{
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows( result );
|
||||||
|
|
||||||
if ( n_users != 1 )
|
if ( n_users != 1 )
|
||||||
{
|
{
|
||||||
Warning( "Unable to authenticate user %s", username );
|
Warning( "Unable to authenticate user %s", username );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
|
|
||||||
User *user = new User( dbrow );
|
User *user = new User( dbrow );
|
||||||
Info( "Authenticated user '%s'", user->getUsername() );
|
Info( "Authenticated user '%s'", user->getUsername() );
|
||||||
|
|
||||||
mysql_free_result( result );
|
mysql_free_result( result );
|
||||||
|
|
||||||
return( user );
|
return( user );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to validate an authentication string
|
// Function to validate an authentication string
|
||||||
|
@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
|
||||||
{
|
{
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal( "Unable to initialise libgcrypt" );
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
const char *remote_addr = "";
|
const char *remote_addr = "";
|
||||||
if ( use_remote_addr )
|
if ( use_remote_addr )
|
||||||
{
|
{
|
||||||
remote_addr = getenv( "REMOTE_ADDR" );
|
remote_addr = getenv( "REMOTE_ADDR" );
|
||||||
if ( !remote_addr )
|
if ( !remote_addr )
|
||||||
{
|
{
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning( "Can't determine remote address, using null" );
|
||||||
remote_addr = "";
|
remote_addr = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result )
|
if ( !result )
|
||||||
{
|
{
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows( result );
|
||||||
|
|
||||||
if ( n_users < 1 )
|
if ( n_users < 1 )
|
||||||
{
|
{
|
||||||
Warning( "Unable to authenticate user" );
|
Warning( "Unable to authenticate user" );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||||
{
|
{
|
||||||
const char *user = dbrow[0];
|
const char *user = dbrow[0];
|
||||||
const char *pass = dbrow[1];
|
const char *pass = dbrow[1];
|
||||||
|
|
||||||
char auth_key[512] = "";
|
char auth_key[512] = "";
|
||||||
char auth_md5[32+1] = "";
|
char auth_md5[32+1] = "";
|
||||||
size_t md5len = 16;
|
size_t md5len = 16;
|
||||||
unsigned char md5sum[md5len];
|
unsigned char md5sum[md5len];
|
||||||
|
|
||||||
time_t now = time( 0 );
|
time_t now = time( 0 );
|
||||||
int max_tries = 2;
|
int max_tries = 2;
|
||||||
|
|
||||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||||
{
|
{
|
||||||
struct tm *now_tm = localtime( &now );
|
struct tm *now_tm = localtime( &now );
|
||||||
|
|
||||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||||
config.auth_hash_secret,
|
config.auth_hash_secret,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
remote_addr,
|
remote_addr,
|
||||||
now_tm->tm_hour,
|
now_tm->tm_hour,
|
||||||
now_tm->tm_mday,
|
now_tm->tm_mday,
|
||||||
now_tm->tm_mon,
|
now_tm->tm_mon,
|
||||||
now_tm->tm_year
|
now_tm->tm_year
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||||
#endif
|
#endif
|
||||||
auth_md5[0] = '\0';
|
auth_md5[0] = '\0';
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||||
}
|
}
|
||||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||||
|
|
||||||
if ( !strcmp( auth, auth_md5 ) )
|
if ( !strcmp( auth, auth_md5 ) )
|
||||||
{
|
{
|
||||||
// We have a match
|
// We have a match
|
||||||
User *user = new User( dbrow );
|
User *user = new User( dbrow );
|
||||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||||
return( user );
|
return( user );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5
|
||||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,33 +39,33 @@
|
||||||
class User
|
class User
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char username[32+1];
|
char username[32+1];
|
||||||
char password[64+1];
|
char password[64+1];
|
||||||
bool enabled;
|
bool enabled;
|
||||||
Permission stream;
|
Permission stream;
|
||||||
Permission events;
|
Permission events;
|
||||||
Permission control;
|
Permission control;
|
||||||
Permission monitors;
|
Permission monitors;
|
||||||
Permission system;
|
Permission system;
|
||||||
int *monitor_ids;
|
int *monitor_ids;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
User();
|
User();
|
||||||
User( MYSQL_ROW &dbrow );
|
User( MYSQL_ROW &dbrow );
|
||||||
~User();
|
~User();
|
||||||
|
|
||||||
const char *getUsername() const { return( username ); }
|
const char *getUsername() const { return( username ); }
|
||||||
const char *getPassword() const { return( password ); }
|
const char *getPassword() const { return( password ); }
|
||||||
bool isEnabled() const { return( enabled ); }
|
bool isEnabled() const { return( enabled ); }
|
||||||
Permission getStream() const { return( stream ); }
|
Permission getStream() const { return( stream ); }
|
||||||
Permission getEvents() const { return( events ); }
|
Permission getEvents() const { return( events ); }
|
||||||
Permission getControl() const { return( control ); }
|
Permission getControl() const { return( control ); }
|
||||||
Permission getMonitors() const { return( monitors ); }
|
Permission getMonitors() const { return( monitors ); }
|
||||||
Permission getSystem() const { return( system ); }
|
Permission getSystem() const { return( system ); }
|
||||||
bool canAccess( int monitor_id );
|
bool canAccess( int monitor_id );
|
||||||
};
|
};
|
||||||
|
|
||||||
User *zmLoadUser( const char *username, const char *password=0 );
|
User *zmLoadUser( const char *username, const char *password=0 );
|
||||||
|
|
450
src/zm_utils.cpp
450
src/zm_utils.cpp
|
@ -28,256 +28,256 @@
|
||||||
unsigned int sseversion = 0;
|
unsigned int sseversion = 0;
|
||||||
|
|
||||||
std::string trimSet(std::string str, std::string trimset) {
|
std::string trimSet(std::string str, std::string trimset) {
|
||||||
// Trim Both leading and trailing sets
|
// Trim Both leading and trailing sets
|
||||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||||
|
|
||||||
// if all spaces or empty return an empty string
|
// if all spaces or empty return an empty string
|
||||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||||
{
|
{
|
||||||
return std::string("");
|
return std::string("");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return str.substr( startpos, endpos-startpos+1 );
|
return str.substr( startpos, endpos-startpos+1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trimSpaces(std::string str)
|
std::string trimSpaces(std::string str)
|
||||||
{
|
{
|
||||||
return trimSet(str, " \t");
|
return trimSet(str, " \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string replaceAll(std::string str, std::string from, std::string to) {
|
std::string replaceAll(std::string str, std::string from, std::string to) {
|
||||||
if(from.empty())
|
if(from.empty())
|
||||||
return str;
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
||||||
str.replace(start_pos, from.length(), to);
|
|
||||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
|
||||||
}
|
|
||||||
return str;
|
return str;
|
||||||
|
size_t start_pos = 0;
|
||||||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||||
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string stringtf( const char *format, ... )
|
const std::string stringtf( const char *format, ... )
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char tempBuffer[8192];
|
char tempBuffer[8192];
|
||||||
std::string tempString;
|
std::string tempString;
|
||||||
|
|
||||||
va_start(ap, format );
|
va_start(ap, format );
|
||||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
tempString = tempBuffer;
|
tempString = tempBuffer;
|
||||||
|
|
||||||
return( tempString );
|
return( tempString );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string stringtf( const std::string &format, ... )
|
const std::string stringtf( const std::string &format, ... )
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char tempBuffer[8192];
|
char tempBuffer[8192];
|
||||||
std::string tempString;
|
std::string tempString;
|
||||||
|
|
||||||
va_start(ap, format );
|
va_start(ap, format );
|
||||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
tempString = tempBuffer;
|
tempString = tempBuffer;
|
||||||
|
|
||||||
return( tempString );
|
return( tempString );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool startsWith( const std::string &haystack, const std::string &needle )
|
bool startsWith( const std::string &haystack, const std::string &needle )
|
||||||
{
|
{
|
||||||
return( haystack.substr( 0, needle.length() ) == needle );
|
return( haystack.substr( 0, needle.length() ) == needle );
|
||||||
}
|
}
|
||||||
|
|
||||||
StringVector split( const std::string &string, const std::string chars, int limit )
|
StringVector split( const std::string &string, const std::string chars, int limit )
|
||||||
{
|
{
|
||||||
StringVector stringVector;
|
StringVector stringVector;
|
||||||
std::string tempString = string;
|
std::string tempString = string;
|
||||||
std::string::size_type startIndex = 0;
|
std::string::size_type startIndex = 0;
|
||||||
std::string::size_type endIndex = 0;
|
std::string::size_type endIndex = 0;
|
||||||
|
|
||||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||||
do
|
do
|
||||||
|
{
|
||||||
|
// Find delimiters
|
||||||
|
endIndex = string.find_first_of( chars, startIndex );
|
||||||
|
//Info( "Got endIndex at %d", endIndex );
|
||||||
|
if ( endIndex > 0 )
|
||||||
{
|
{
|
||||||
// Find delimiters
|
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||||
endIndex = string.find_first_of( chars, startIndex );
|
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||||
//Info( "Got endIndex at %d", endIndex );
|
}
|
||||||
if ( endIndex > 0 )
|
if ( endIndex == std::string::npos )
|
||||||
{
|
break;
|
||||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
// Find non-delimiters
|
||||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||||
}
|
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||||
if ( endIndex == std::string::npos )
|
{
|
||||||
break;
|
stringVector.push_back( string.substr( startIndex ) );
|
||||||
// Find non-delimiters
|
break;
|
||||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
}
|
||||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
//Info( "Got new startIndex at %d", startIndex );
|
||||||
{
|
} while ( startIndex != std::string::npos );
|
||||||
stringVector.push_back( string.substr( startIndex ) );
|
//Info( "Finished with %d strings", stringVector.size() );
|
||||||
break;
|
|
||||||
}
|
|
||||||
//Info( "Got new startIndex at %d", startIndex );
|
|
||||||
} while ( startIndex != std::string::npos );
|
|
||||||
//Info( "Finished with %d strings", stringVector.size() );
|
|
||||||
|
|
||||||
return( stringVector );
|
return( stringVector );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string join(const StringVector v, const char * delim ) {
|
const std::string join(const StringVector v, const char * delim ) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
for(size_t i = 0; i < v.size(); ++i) {
|
for(size_t i = 0; i < v.size(); ++i) {
|
||||||
if(i != 0)
|
if(i != 0)
|
||||||
ss << ",";
|
ss << ",";
|
||||||
ss << v[i];
|
ss << v[i];
|
||||||
}
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string base64Encode( const std::string &inString )
|
const std::string base64Encode( const std::string &inString )
|
||||||
{
|
{
|
||||||
static char base64_table[64] = { '\0' };
|
static char base64_table[64] = { '\0' };
|
||||||
|
|
||||||
if ( !base64_table[0] )
|
if ( !base64_table[0] )
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
for ( char c = 'a'; c <= 'z'; c++ )
|
for ( char c = 'a'; c <= 'z'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
for ( char c = '0'; c <= '9'; c++ )
|
for ( char c = '0'; c <= '9'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
base64_table[i++] = '+';
|
base64_table[i++] = '+';
|
||||||
base64_table[i++] = '/';
|
base64_table[i++] = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string outString;
|
std::string outString;
|
||||||
outString.reserve( 2 * inString.size() );
|
outString.reserve( 2 * inString.size() );
|
||||||
|
|
||||||
const char *inPtr = inString.c_str();
|
const char *inPtr = inString.c_str();
|
||||||
while( *inPtr )
|
while( *inPtr )
|
||||||
{
|
{
|
||||||
unsigned char selection = *inPtr >> 2;
|
unsigned char selection = *inPtr >> 2;
|
||||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
|
||||||
if ( *inPtr )
|
if ( *inPtr )
|
||||||
{
|
{
|
||||||
selection = remainder | (*inPtr >> 4);
|
selection = remainder | (*inPtr >> 4);
|
||||||
remainder = (*inPtr++ & 0x0f) << 2;
|
remainder = (*inPtr++ & 0x0f) << 2;
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
|
||||||
if ( *inPtr )
|
if ( *inPtr )
|
||||||
{
|
{
|
||||||
selection = remainder | (*inPtr >> 6);
|
selection = remainder | (*inPtr >> 6);
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
selection = (*inPtr++ & 0x3f);
|
selection = (*inPtr++ & 0x3f);
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outString += base64_table[remainder];
|
outString += base64_table[remainder];
|
||||||
outString += '=';
|
outString += '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outString += base64_table[remainder];
|
outString += base64_table[remainder];
|
||||||
outString += '=';
|
outString += '=';
|
||||||
outString += '=';
|
outString += '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( outString );
|
return( outString );
|
||||||
}
|
}
|
||||||
|
|
||||||
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
||||||
if(string == NULL)
|
if(string == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(string[0] == 0)
|
if(string[0] == 0)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
std::string str(string);
|
std::string str(string);
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
pos = str.find(delim);
|
pos = str.find(delim);
|
||||||
items.push_back(str.substr(0, pos));
|
items.push_back(str.substr(0, pos));
|
||||||
str.erase(0, pos+1);
|
str.erase(0, pos+1);
|
||||||
|
|
||||||
if(pos == std::string::npos)
|
if(pos == std::string::npos)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.size();
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
||||||
if(string == NULL)
|
if(string == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(string[0] == 0)
|
if(string[0] == 0)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
std::string str(string);
|
std::string str(string);
|
||||||
size_t pos = str.find(delim);
|
size_t pos = str.find(delim);
|
||||||
|
|
||||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||||
return -3;
|
return -3;
|
||||||
|
|
||||||
name = str.substr(0, pos);
|
name = str.substr(0, pos);
|
||||||
value = str.substr(pos+1, std::string::npos);
|
value = str.substr(pos+1, std::string::npos);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets sse_version */
|
/* Sets sse_version */
|
||||||
void ssedetect() {
|
void ssedetect() {
|
||||||
#if (defined(__i386__) || defined(__x86_64__))
|
#if (defined(__i386__) || defined(__x86_64__))
|
||||||
/* x86 or x86-64 processor */
|
/* x86 or x86-64 processor */
|
||||||
uint32_t r_edx, r_ecx;
|
uint32_t r_edx, r_ecx;
|
||||||
|
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
"pushl %%ebx;\n\t"
|
"pushl %%ebx;\n\t"
|
||||||
#endif
|
#endif
|
||||||
"mov $0x1,%%eax\n\t"
|
"mov $0x1,%%eax\n\t"
|
||||||
"cpuid\n\t"
|
"cpuid\n\t"
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
"popl %%ebx;\n\t"
|
"popl %%ebx;\n\t"
|
||||||
#endif
|
#endif
|
||||||
: "=d" (r_edx), "=c" (r_ecx)
|
: "=d" (r_edx), "=c" (r_ecx)
|
||||||
:
|
:
|
||||||
: "%eax"
|
: "%eax"
|
||||||
#if !defined(__i386__)
|
#if !defined(__i386__)
|
||||||
, "%ebx"
|
, "%ebx"
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
if (r_ecx & 0x00000200) {
|
if (r_ecx & 0x00000200) {
|
||||||
sseversion = 35; /* SSSE3 */
|
sseversion = 35; /* SSSE3 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||||
} else if (r_ecx & 0x00000001) {
|
} else if (r_ecx & 0x00000001) {
|
||||||
sseversion = 30; /* SSE3 */
|
sseversion = 30; /* SSE3 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||||
} else if (r_edx & 0x04000000) {
|
} else if (r_edx & 0x04000000) {
|
||||||
sseversion = 20; /* SSE2 */
|
sseversion = 20; /* SSE2 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||||
} else if (r_edx & 0x02000000) {
|
} else if (r_edx & 0x02000000) {
|
||||||
sseversion = 10; /* SSE */
|
sseversion = 10; /* SSE */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||||
} else {
|
} else {
|
||||||
sseversion = 0;
|
sseversion = 0;
|
||||||
Debug(1,"Detected a x86\\x86-64 processor");
|
Debug(1,"Detected a x86\\x86-64 processor");
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||||
sseversion = 0;
|
sseversion = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
|
||||||
#endif
|
#endif
|
||||||
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
||||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||||
if(bytes > 128) {
|
if(bytes > 128) {
|
||||||
unsigned int remainder = bytes % 128;
|
unsigned int remainder = bytes % 128;
|
||||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||||
|
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"sse2_copy_iter:\n\t"
|
"sse2_copy_iter:\n\t"
|
||||||
"movdqa (%0),%%xmm0\n\t"
|
"movdqa (%0),%%xmm0\n\t"
|
||||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||||
"movntdq %%xmm0,(%1)\n\t"
|
"movntdq %%xmm0,(%1)\n\t"
|
||||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||||
"add $0x80, %0\n\t"
|
"add $0x80, %0\n\t"
|
||||||
"add $0x80, %1\n\t"
|
"add $0x80, %1\n\t"
|
||||||
"cmp %2, %0\n\t"
|
"cmp %2, %0\n\t"
|
||||||
"jb sse2_copy_iter\n\t"
|
"jb sse2_copy_iter\n\t"
|
||||||
"test %3, %3\n\t"
|
"test %3, %3\n\t"
|
||||||
"jz sse2_copy_finish\n\t"
|
"jz sse2_copy_finish\n\t"
|
||||||
"cld\n\t"
|
"cld\n\t"
|
||||||
"rep movsb\n\t"
|
"rep movsb\n\t"
|
||||||
"sse2_copy_finish:\n\t"
|
"sse2_copy_finish:\n\t"
|
||||||
:
|
:
|
||||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Standard memcpy */
|
/* Standard memcpy */
|
||||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Non x86\x86-64 platform, use memcpy */
|
/* Non x86\x86-64 platform, use memcpy */
|
||||||
memcpy(dest,src,bytes);
|
memcpy(dest,src,bytes);
|
||||||
#endif
|
#endif
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
||||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||||
} else {
|
} else {
|
||||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
|
||||||
|
|
||||||
inline int max( int a, int b )
|
inline int max( int a, int b )
|
||||||
{
|
{
|
||||||
return( a>=b?a:b );
|
return( a>=b?a:b );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int min( int a, int b )
|
inline int min( int a, int b )
|
||||||
{
|
{
|
||||||
return( a<=b?a:b );
|
return( a<=b?a:b );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssedetect();
|
void ssedetect();
|
||||||
|
|
228
src/zm_zone.h
228
src/zm_zone.h
|
@ -35,141 +35,141 @@ class Monitor;
|
||||||
class Zone
|
class Zone
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct Range
|
struct Range
|
||||||
{
|
{
|
||||||
int lo_x;
|
int lo_x;
|
||||||
int hi_x;
|
int hi_x;
|
||||||
int off_x;
|
int off_x;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Inputs
|
// Inputs
|
||||||
Monitor *monitor;
|
Monitor *monitor;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
char *label;
|
char *label;
|
||||||
ZoneType type;
|
ZoneType type;
|
||||||
Polygon polygon;
|
Polygon polygon;
|
||||||
Rgb alarm_rgb;
|
Rgb alarm_rgb;
|
||||||
CheckMethod check_method;
|
CheckMethod check_method;
|
||||||
|
|
||||||
int min_pixel_threshold;
|
int min_pixel_threshold;
|
||||||
int max_pixel_threshold;
|
int max_pixel_threshold;
|
||||||
|
|
||||||
int min_alarm_pixels;
|
int min_alarm_pixels;
|
||||||
int max_alarm_pixels;
|
int max_alarm_pixels;
|
||||||
|
|
||||||
Coord filter_box;
|
Coord filter_box;
|
||||||
int min_filter_pixels;
|
int min_filter_pixels;
|
||||||
int max_filter_pixels;
|
int max_filter_pixels;
|
||||||
|
|
||||||
int min_blob_pixels;
|
int min_blob_pixels;
|
||||||
int max_blob_pixels;
|
int max_blob_pixels;
|
||||||
int min_blobs;
|
int min_blobs;
|
||||||
int max_blobs;
|
int max_blobs;
|
||||||
|
|
||||||
int overload_frames;
|
int overload_frames;
|
||||||
int extend_alarm_frames;
|
int extend_alarm_frames;
|
||||||
|
|
||||||
// Outputs/Statistics
|
// Outputs/Statistics
|
||||||
bool alarmed;
|
bool alarmed;
|
||||||
int pixel_diff;
|
int pixel_diff;
|
||||||
unsigned int alarm_pixels;
|
unsigned int alarm_pixels;
|
||||||
int alarm_filter_pixels;
|
int alarm_filter_pixels;
|
||||||
int alarm_blob_pixels;
|
int alarm_blob_pixels;
|
||||||
int alarm_blobs;
|
int alarm_blobs;
|
||||||
int min_blob_size;
|
int min_blob_size;
|
||||||
int max_blob_size;
|
int max_blob_size;
|
||||||
Box alarm_box;
|
Box alarm_box;
|
||||||
Coord alarm_centre;
|
Coord alarm_centre;
|
||||||
unsigned int score;
|
unsigned int score;
|
||||||
Image *pg_image;
|
Image *pg_image;
|
||||||
Range *ranges;
|
Range *ranges;
|
||||||
Image *image;
|
Image *image;
|
||||||
|
|
||||||
int overload_count;
|
int overload_count;
|
||||||
int extend_alarm_count;
|
int extend_alarm_count;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Zone();
|
~Zone();
|
||||||
|
|
||||||
inline int Id() const { return( id ); }
|
inline int Id() const { return( id ); }
|
||||||
inline const char *Label() const { return( label ); }
|
inline const char *Label() const { return( label ); }
|
||||||
inline ZoneType Type() const { return( type ); }
|
inline ZoneType Type() const { return( type ); }
|
||||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||||
inline const Image *AlarmImage() const { return( image ); }
|
inline const Image *AlarmImage() const { return( image ); }
|
||||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||||
inline bool Alarmed() const { return( alarmed ); }
|
inline bool Alarmed() const { return( alarmed ); }
|
||||||
inline void SetAlarm() { alarmed = true; }
|
inline void SetAlarm() { alarmed = true; }
|
||||||
inline void ClearAlarm() { alarmed = false; }
|
inline void ClearAlarm() { alarmed = false; }
|
||||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||||
inline unsigned int Score() const { return( score ); }
|
inline unsigned int Score() const { return( score ); }
|
||||||
|
|
||||||
inline void ResetStats()
|
inline void ResetStats()
|
||||||
{
|
{
|
||||||
alarmed = false;
|
alarmed = false;
|
||||||
pixel_diff = 0;
|
pixel_diff = 0;
|
||||||
alarm_pixels = 0;
|
alarm_pixels = 0;
|
||||||
alarm_filter_pixels = 0;
|
alarm_filter_pixels = 0;
|
||||||
alarm_blob_pixels = 0;
|
alarm_blob_pixels = 0;
|
||||||
alarm_blobs = 0;
|
alarm_blobs = 0;
|
||||||
min_blob_size = 0;
|
min_blob_size = 0;
|
||||||
max_blob_size = 0;
|
max_blob_size = 0;
|
||||||
score = 0;
|
score = 0;
|
||||||
}
|
}
|
||||||
void RecordStats( const Event *event );
|
void RecordStats( const Event *event );
|
||||||
bool CheckAlarms( const Image *delta_image );
|
bool CheckAlarms( const Image *delta_image );
|
||||||
bool DumpSettings( char *output, bool verbose );
|
bool DumpSettings( char *output, bool verbose );
|
||||||
|
|
||||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||||
static int Load( Monitor *monitor, Zone **&zones );
|
static int Load( Monitor *monitor, Zone **&zones );
|
||||||
//=================================================
|
//=================================================
|
||||||
bool CheckOverloadCount();
|
bool CheckOverloadCount();
|
||||||
int GetOverloadCount();
|
int GetOverloadCount();
|
||||||
void SetOverloadCount(int nOverCount);
|
void SetOverloadCount(int nOverCount);
|
||||||
int GetOverloadFrames();
|
int GetOverloadFrames();
|
||||||
//=================================================
|
//=================================================
|
||||||
bool CheckExtendAlarmCount();
|
bool CheckExtendAlarmCount();
|
||||||
int GetExtendAlarmCount();
|
int GetExtendAlarmCount();
|
||||||
void SetExtendAlarmCount(int nOverCount);
|
void SetExtendAlarmCount(int nOverCount);
|
||||||
int GetExtendAlarmFrames();
|
int GetExtendAlarmFrames();
|
||||||
void SetScore(unsigned int nScore);
|
void SetScore(unsigned int nScore);
|
||||||
void SetAlarmImage(const Image* srcImage);
|
void SetAlarmImage(const Image* srcImage);
|
||||||
|
|
||||||
inline const Image *getPgImage() const { return( pg_image ); }
|
inline const Image *getPgImage() const { return( pg_image ); }
|
||||||
inline const Range *getRanges() const { return( ranges ); }
|
inline const Range *getRanges() const { return( ranges ); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
240
src/zma.cpp
240
src/zma.cpp
|
@ -41,9 +41,9 @@ behind.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-m, --monitor_id - ID of the monitor to analyse
|
-m, --monitor_id - ID of the monitor to analyse
|
||||||
-h, --help - Display usage information
|
-h, --help - Display usage information
|
||||||
-v, --version - Print the installed version of ZoneMinder
|
-v, --version - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
@ -59,146 +59,146 @@ behind.
|
||||||
|
|
||||||
void Usage()
|
void Usage()
|
||||||
{
|
{
|
||||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||||
fprintf( stderr, "Options:\n" );
|
fprintf( stderr, "Options:\n" );
|
||||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||||
fprintf( stderr, " -h, --help : This screen\n" );
|
fprintf( stderr, " -h, --help : This screen\n" );
|
||||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, char *argv[] )
|
int main( int argc, char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
int id = -1;
|
int id = -1;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"monitor", 1, 0, 'm'},
|
{"monitor", 1, 0, 'm'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, 'v'},
|
{"version", 0, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'm':
|
case 'm':
|
||||||
id = atoi(optarg);
|
id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
Usage();
|
Usage();
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
std::cout << ZM_VERSION << "\n";
|
std::cout << ZM_VERSION << "\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc)
|
if (optind < argc)
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Extraneous options, " );
|
fprintf( stderr, "Extraneous options, " );
|
||||||
while (optind < argc)
|
while (optind < argc)
|
||||||
printf ("%s ", argv[optind++]);
|
printf ("%s ", argv[optind++]);
|
||||||
printf ("\n");
|
printf ("\n");
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( id < 0 )
|
if ( id < 0 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_id_string[16];
|
char log_id_string[16];
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( log_id_string );
|
logInit( log_id_string );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||||
|
|
||||||
if ( monitor )
|
if ( monitor )
|
||||||
{
|
{
|
||||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||||
|
|
||||||
if ( config.opt_frame_server )
|
if ( config.opt_frame_server )
|
||||||
{
|
{
|
||||||
Event::OpenFrameSocket( monitor->Id() );
|
Event::OpenFrameSocket( monitor->Id() );
|
||||||
}
|
}
|
||||||
|
|
||||||
zmSetDefaultHupHandler();
|
zmSetDefaultHupHandler();
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
sigemptyset( &block_set );
|
sigemptyset( &block_set );
|
||||||
|
|
||||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||||
time_t last_analysis_update_time, cur_time;
|
time_t last_analysis_update_time, cur_time;
|
||||||
monitor->UpdateAdaptiveSkip();
|
monitor->UpdateAdaptiveSkip();
|
||||||
last_analysis_update_time = time( 0 );
|
last_analysis_update_time = time( 0 );
|
||||||
|
|
||||||
while( !zm_terminate )
|
while( !zm_terminate )
|
||||||
{
|
{
|
||||||
// Process the next image
|
// Process the next image
|
||||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||||
|
|
||||||
// Some periodic updates are required for variable capturing framerate
|
// Some periodic updates are required for variable capturing framerate
|
||||||
if ( analysis_update_delay )
|
if ( analysis_update_delay )
|
||||||
{
|
{
|
||||||
cur_time = time( 0 );
|
cur_time = time( 0 );
|
||||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||||
{
|
{
|
||||||
analysis_rate = monitor->GetAnalysisRate();
|
analysis_rate = monitor->GetAnalysisRate();
|
||||||
monitor->UpdateAdaptiveSkip();
|
monitor->UpdateAdaptiveSkip();
|
||||||
last_analysis_update_time = cur_time;
|
last_analysis_update_time = cur_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !monitor->Analyse() )
|
if ( !monitor->Analyse() )
|
||||||
{
|
{
|
||||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||||
}
|
}
|
||||||
else if ( analysis_rate )
|
else if ( analysis_rate )
|
||||||
{
|
{
|
||||||
usleep( analysis_rate );
|
usleep( analysis_rate );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( zm_reload )
|
if ( zm_reload )
|
||||||
{
|
{
|
||||||
monitor->Reload();
|
monitor->Reload();
|
||||||
zm_reload = false;
|
zm_reload = false;
|
||||||
}
|
}
|
||||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||||
}
|
}
|
||||||
delete monitor;
|
delete monitor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||||
}
|
}
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
510
src/zmc.cpp
510
src/zmc.cpp
|
@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||||
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
||||||
-f, --file <file_path> - For local images, jpg file to access.
|
-f, --file <file_path> - For local images, jpg file to access.
|
||||||
-m, --monitor_id - ID of the monitor to analyse
|
-m, --monitor_id - ID of the monitor to analyse
|
||||||
-h, --help - Display usage information
|
-h, --help - Display usage information
|
||||||
-v, --version - Print the installed version of ZoneMinder
|
-v, --version - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
|
||||||
|
|
||||||
void Usage()
|
void Usage()
|
||||||
{
|
{
|
||||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||||
|
|
||||||
fprintf( stderr, "Options:\n" );
|
fprintf( stderr, "Options:\n" );
|
||||||
#if defined(BSD)
|
#if defined(BSD)
|
||||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||||
#else
|
#else
|
||||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||||
#endif
|
#endif
|
||||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||||
fprintf( stderr, " -h, --help : This screen\n" );
|
fprintf( stderr, " -h, --help : This screen\n" );
|
||||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, char *argv[] )
|
int main( int argc, char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
const char *device = "";
|
const char *device = "";
|
||||||
const char *protocol = "";
|
const char *protocol = "";
|
||||||
const char *host = "";
|
const char *host = "";
|
||||||
const char *port = "";
|
const char *port = "";
|
||||||
const char *path = "";
|
const char *path = "";
|
||||||
const char *file = "";
|
const char *file = "";
|
||||||
int monitor_id = -1;
|
int monitor_id = -1;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"device", 1, 0, 'd'},
|
{"device", 1, 0, 'd'},
|
||||||
{"protocol", 1, 0, 'r'},
|
{"protocol", 1, 0, 'r'},
|
||||||
{"host", 1, 0, 'H'},
|
{"host", 1, 0, 'H'},
|
||||||
{"port", 1, 0, 'P'},
|
{"port", 1, 0, 'P'},
|
||||||
{"path", 1, 0, 'p'},
|
{"path", 1, 0, 'p'},
|
||||||
{"file", 1, 0, 'f'},
|
{"file", 1, 0, 'f'},
|
||||||
{"monitor", 1, 0, 'm'},
|
{"monitor", 1, 0, 'm'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, 'v'},
|
{"version", 0, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'd':
|
case 'd':
|
||||||
device = optarg;
|
device = optarg;
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
host = optarg;
|
host = optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
port = optarg;
|
port = optarg;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
path = optarg;
|
path = optarg;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
file = optarg;
|
file = optarg;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
monitor_id = atoi(optarg);
|
monitor_id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
Usage();
|
Usage();
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
std::cout << ZM_VERSION << "\n";
|
std::cout << ZM_VERSION << "\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc)
|
if (optind < argc)
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Extraneous options, " );
|
fprintf( stderr, "Extraneous options, " );
|
||||||
while (optind < argc)
|
while (optind < argc)
|
||||||
printf ("%s ", argv[optind++]);
|
printf ("%s ", argv[optind++]);
|
||||||
printf ("\n");
|
printf ("\n");
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||||
if ( modes > 1 )
|
if ( modes > 1 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( modes < 1 )
|
if ( modes < 1 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_id_string[32] = "";
|
char log_id_string[32] = "";
|
||||||
if ( device[0] )
|
if ( device[0] )
|
||||||
{
|
{
|
||||||
const char *slash_ptr = strrchr( device, '/' );
|
const char *slash_ptr = strrchr( device, '/' );
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||||
}
|
}
|
||||||
else if ( host[0] )
|
else if ( host[0] )
|
||||||
{
|
{
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||||
}
|
}
|
||||||
else if ( file[0] )
|
else if ( file[0] )
|
||||||
{
|
{
|
||||||
const char *slash_ptr = strrchr( file, '/' );
|
const char *slash_ptr = strrchr( file, '/' );
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( log_id_string );
|
logInit( log_id_string );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
Monitor **monitors = 0;
|
Monitor **monitors = 0;
|
||||||
int n_monitors = 0;
|
int n_monitors = 0;
|
||||||
#if ZM_HAS_V4L
|
#if ZM_HAS_V4L
|
||||||
if ( device[0] )
|
if ( device[0] )
|
||||||
{
|
{
|
||||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
if ( host[0] )
|
if ( host[0] )
|
||||||
{
|
{
|
||||||
if ( !port )
|
if ( !port )
|
||||||
port = "80";
|
port = "80";
|
||||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else if ( file[0] )
|
else if ( file[0] )
|
||||||
{
|
{
|
||||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||||
if ( monitor )
|
if ( monitor )
|
||||||
{
|
{
|
||||||
monitors = new Monitor *[1];
|
monitors = new Monitor *[1];
|
||||||
monitors[0] = monitor;
|
monitors[0] = monitor;
|
||||||
n_monitors = 1;
|
n_monitors = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !n_monitors )
|
if ( !n_monitors )
|
||||||
{
|
{
|
||||||
Error( "No monitors found" );
|
Error( "No monitors found" );
|
||||||
exit ( -1 );
|
exit ( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Info( "Starting Capture version %s", ZM_VERSION );
|
Info( "Starting Capture version %s", ZM_VERSION );
|
||||||
|
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
sigemptyset( &block_set );
|
sigemptyset( &block_set );
|
||||||
|
|
||||||
sigaddset( &block_set, SIGUSR1 );
|
sigaddset( &block_set, SIGUSR1 );
|
||||||
sigaddset( &block_set, SIGUSR2 );
|
sigaddset( &block_set, SIGUSR2 );
|
||||||
|
|
||||||
if ( monitors[0]->PrimeCapture() < 0 )
|
if ( monitors[0]->PrimeCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to prime capture of initial monitor" );
|
Error( "Failed to prime capture of initial monitor" );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
long *capture_delays = new long[n_monitors];
|
long *capture_delays = new long[n_monitors];
|
||||||
long *alarm_capture_delays = new long[n_monitors];
|
long *alarm_capture_delays = new long[n_monitors];
|
||||||
long *next_delays = new long[n_monitors];
|
long *next_delays = new long[n_monitors];
|
||||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
struct DeltaTimeval delta_time;
|
struct DeltaTimeval delta_time;
|
||||||
while( !zm_terminate )
|
while( !zm_terminate )
|
||||||
{
|
{
|
||||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
long min_delay = MAXINT;
|
long min_delay = MAXINT;
|
||||||
|
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
for ( int j = 0; j < n_monitors; j++ )
|
for ( int j = 0; j < n_monitors; j++ )
|
||||||
{
|
{
|
||||||
if ( last_capture_times[j].tv_sec )
|
if ( last_capture_times[j].tv_sec )
|
||||||
{
|
{
|
||||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||||
else
|
else
|
||||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||||
if ( next_delays[j] < 0 )
|
if ( next_delays[j] < 0 )
|
||||||
next_delays[j] = 0;
|
next_delays[j] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
next_delays[j] = 0;
|
next_delays[j] = 0;
|
||||||
}
|
}
|
||||||
if ( next_delays[j] <= min_delay )
|
if ( next_delays[j] <= min_delay )
|
||||||
{
|
{
|
||||||
min_delay = next_delays[j];
|
min_delay = next_delays[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||||
{
|
{
|
||||||
if ( monitors[i]->PreCapture() < 0 )
|
if ( monitors[i]->PreCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( monitors[i]->Capture() < 0 )
|
if ( monitors[i]->Capture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( monitors[i]->PostCapture() < 0 )
|
if ( monitors[i]->PostCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( next_delays[i] > 0 )
|
if ( next_delays[i] > 0 )
|
||||||
{
|
{
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||||
long sleep_time = next_delays[i]-delta_time.delta;
|
long sleep_time = next_delays[i]-delta_time.delta;
|
||||||
if ( sleep_time > 0 )
|
if ( sleep_time > 0 )
|
||||||
{
|
{
|
||||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gettimeofday( &(last_capture_times[i]), NULL );
|
gettimeofday( &(last_capture_times[i]), NULL );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
delete monitors[i];
|
delete monitors[i];
|
||||||
}
|
}
|
||||||
delete [] monitors;
|
delete [] monitors;
|
||||||
delete [] alarm_capture_delays;
|
delete [] alarm_capture_delays;
|
||||||
delete [] capture_delays;
|
delete [] capture_delays;
|
||||||
delete [] next_delays;
|
delete [] next_delays;
|
||||||
delete [] last_capture_times;
|
delete [] last_capture_times;
|
||||||
|
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
|
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
10
src/zmf.h
10
src/zmf.h
|
@ -22,11 +22,11 @@
|
||||||
|
|
||||||
struct FrameHeader
|
struct FrameHeader
|
||||||
{
|
{
|
||||||
unsigned long event_id;
|
unsigned long event_id;
|
||||||
time_t event_time;
|
time_t event_time;
|
||||||
unsigned long frame_id;
|
unsigned long frame_id;
|
||||||
bool alarm_frame;
|
bool alarm_frame;
|
||||||
unsigned long image_length;
|
unsigned long image_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZMFILE_H
|
#endif // ZMFILE_H
|
||||||
|
|
|
@ -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 );
|
Error("disk_total_space returned false for " . ZM_DIR_EVENTS );
|
||||||
return 0;
|
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 );
|
return( $space );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue