tabs to spaces and use 2space indenting
This commit is contained in:
parent
9276eea491
commit
03b1ced568
4
src/zm.h
4
src/zm.h
|
@ -26,8 +26,8 @@
|
|||
|
||||
#include "zm_config.h"
|
||||
#ifdef SOLARIS
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||
#include <string.h> // define strerror() and friends
|
||||
#endif
|
||||
#include "zm_logger.h"
|
||||
|
||||
|
|
12296
src/zm_bigfont.h
12296
src/zm_bigfont.h
File diff suppressed because it is too large
Load Diff
58
src/zm_box.h
58
src/zm_box.h
|
@ -36,39 +36,39 @@
|
|||
class Box
|
||||
{
|
||||
private:
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
Coord lo, hi;
|
||||
Coord size;
|
||||
|
||||
public:
|
||||
inline Box()
|
||||
{
|
||||
}
|
||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box()
|
||||
{
|
||||
}
|
||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
inline const Coord &Lo() const { return( lo ); }
|
||||
inline int LoX() const { return( lo.X() ); }
|
||||
inline int LoY() const { return( lo.Y() ); }
|
||||
inline const Coord &Hi() const { return( hi ); }
|
||||
inline int HiX() const { return( hi.X() ); }
|
||||
inline int HiY() const { return( hi.Y() ); }
|
||||
inline const Coord &Size() const { return( size ); }
|
||||
inline int Width() const { return( size.X() ); }
|
||||
inline int Height() const { return( size.Y() ); }
|
||||
inline int Area() const { return( size.X()*size.Y() ); }
|
||||
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
inline const Coord Centre() const
|
||||
{
|
||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||
return( Coord( mid_x, mid_y ) );
|
||||
}
|
||||
inline bool Inside( const Coord &coord ) const
|
||||
{
|
||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_BOX_H
|
||||
|
|
|
@ -25,57 +25,57 @@
|
|||
|
||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
if ( mAllocation < pSize )
|
||||
{
|
||||
delete[] mStorage;
|
||||
mAllocation = pSize;
|
||||
mHead = mStorage = new unsigned char[pSize];
|
||||
}
|
||||
mSize = pSize;
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
unsigned int Buffer::expand( unsigned int count )
|
||||
{
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
int spare = mAllocation - mSize;
|
||||
int headSpace = mHead - mStorage;
|
||||
int tailSpace = spare - headSpace;
|
||||
int width = mTail - mHead;
|
||||
if ( spare > (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
if ( tailSpace < (int)count )
|
||||
{
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
memmove( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
mAllocation += count;
|
||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||
if ( mStorage )
|
||||
{
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
memcpy( newStorage, mHead, mSize );
|
||||
delete[] mStorage;
|
||||
}
|
||||
return( mSize );
|
||||
mStorage = newStorage;
|
||||
mHead = mStorage;
|
||||
mTail = mHead + width;
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
|
||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
int bytes_read = read( sd, mTail, bytes );
|
||||
if ( bytes_read > 0 ) {
|
||||
mTail += bytes_read;
|
||||
mSize += bytes_read;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
|
332
src/zm_buffer.h
332
src/zm_buffer.h
|
@ -27,183 +27,183 @@
|
|||
class Buffer
|
||||
{
|
||||
protected:
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
unsigned char *mStorage;
|
||||
unsigned int mAllocation;
|
||||
unsigned int mSize;
|
||||
unsigned char *mHead;
|
||||
unsigned char *mTail;
|
||||
|
||||
public:
|
||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||
{
|
||||
}
|
||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mAllocation];
|
||||
mTail = mHead;
|
||||
}
|
||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, buffer.mHead, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
~Buffer()
|
||||
{
|
||||
delete[] mStorage;
|
||||
}
|
||||
unsigned char *head() const { return( mHead ); }
|
||||
unsigned char *tail() const { return( mTail ); }
|
||||
unsigned int size() const { return( mSize ); }
|
||||
bool empty() const { return( mSize == 0 ); }
|
||||
unsigned int size( unsigned int pSize )
|
||||
{
|
||||
if ( mSize < pSize )
|
||||
{
|
||||
expand( pSize-mSize );
|
||||
}
|
||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mAllocation];
|
||||
mTail = mHead;
|
||||
}
|
||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, pStorage, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||
{
|
||||
mHead = mStorage = new unsigned char[mSize];
|
||||
memcpy( mStorage, buffer.mHead, mSize );
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
~Buffer()
|
||||
{
|
||||
delete[] mStorage;
|
||||
}
|
||||
unsigned char *head() const { return( mHead ); }
|
||||
unsigned char *tail() const { return( mTail ); }
|
||||
unsigned int size() const { return( mSize ); }
|
||||
bool empty() const { return( mSize == 0 ); }
|
||||
unsigned int size( unsigned int pSize )
|
||||
{
|
||||
if ( mSize < pSize )
|
||||
{
|
||||
expand( pSize-mSize );
|
||||
}
|
||||
return( mSize );
|
||||
}
|
||||
//unsigned int Allocation() const { return( mAllocation ); }
|
||||
return( mSize );
|
||||
}
|
||||
//unsigned int Allocation() const { return( mAllocation ); }
|
||||
|
||||
void clear()
|
||||
void clear()
|
||||
{
|
||||
mSize = 0;
|
||||
mHead = mTail = mStorage;
|
||||
}
|
||||
|
||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||
unsigned int assign( const Buffer &buffer )
|
||||
{
|
||||
return( assign( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
|
||||
// Trim from the front of the buffer
|
||||
unsigned int consume( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
mSize = 0;
|
||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mHead += count;
|
||||
mSize -= count;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Trim from the end of the buffer
|
||||
unsigned int shrink( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mSize -= count;
|
||||
if ( mTail > (mHead + mSize) )
|
||||
mTail = mHead + mSize;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Add to the end of the buffer
|
||||
unsigned int expand( unsigned int count );
|
||||
|
||||
// Return pointer to the first pSize bytes and advance the head
|
||||
unsigned char *extract( unsigned int pSize )
|
||||
{
|
||||
if ( pSize > mSize )
|
||||
{
|
||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||
pSize = mSize;
|
||||
}
|
||||
unsigned char *oldHead = mHead;
|
||||
mHead += pSize;
|
||||
mSize -= pSize;
|
||||
tidy( 0 );
|
||||
return( oldHead );
|
||||
}
|
||||
// Add bytes to the end of the buffer
|
||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
expand( pSize );
|
||||
memcpy( mTail, pStorage, pSize );
|
||||
mTail += pSize;
|
||||
mSize += pSize;
|
||||
return( mSize );
|
||||
}
|
||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||
{
|
||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||
}
|
||||
unsigned int append( const Buffer &buffer )
|
||||
{
|
||||
return( append( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
void tidy( bool level=0 )
|
||||
{
|
||||
if ( mHead != mStorage )
|
||||
{
|
||||
if ( mSize == 0 )
|
||||
mHead = mTail = mStorage;
|
||||
}
|
||||
|
||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||
unsigned int assign( const Buffer &buffer )
|
||||
{
|
||||
return( assign( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
|
||||
// Trim from the front of the buffer
|
||||
unsigned int consume( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
memcpy( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
mHead += count;
|
||||
mSize -= count;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
}
|
||||
// Trim from the end of the buffer
|
||||
unsigned int shrink( unsigned int count )
|
||||
{
|
||||
if ( count > mSize )
|
||||
{
|
||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||
count = mSize;
|
||||
}
|
||||
mSize -= count;
|
||||
if ( mTail > (mHead + mSize) )
|
||||
mTail = mHead + mSize;
|
||||
tidy( 0 );
|
||||
return( count );
|
||||
}
|
||||
// Add to the end of the buffer
|
||||
unsigned int expand( unsigned int count );
|
||||
}
|
||||
|
||||
// Return pointer to the first pSize bytes and advance the head
|
||||
unsigned char *extract( unsigned int pSize )
|
||||
{
|
||||
if ( pSize > mSize )
|
||||
{
|
||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||
pSize = mSize;
|
||||
}
|
||||
unsigned char *oldHead = mHead;
|
||||
mHead += pSize;
|
||||
mSize -= pSize;
|
||||
tidy( 0 );
|
||||
return( oldHead );
|
||||
}
|
||||
// Add bytes to the end of the buffer
|
||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||
{
|
||||
expand( pSize );
|
||||
memcpy( mTail, pStorage, pSize );
|
||||
mTail += pSize;
|
||||
mSize += pSize;
|
||||
return( mSize );
|
||||
}
|
||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||
{
|
||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||
}
|
||||
unsigned int append( const Buffer &buffer )
|
||||
{
|
||||
return( append( buffer.mHead, buffer.mSize ) );
|
||||
}
|
||||
void tidy( bool level=0 )
|
||||
{
|
||||
if ( mHead != mStorage )
|
||||
{
|
||||
if ( mSize == 0 )
|
||||
mHead = mTail = mStorage;
|
||||
else if ( level )
|
||||
{
|
||||
if ( (mHead-mStorage) > mSize )
|
||||
{
|
||||
memcpy( mStorage, mHead, mSize );
|
||||
mHead = mStorage;
|
||||
mTail = mHead + mSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Buffer &operator=( const Buffer &buffer )
|
||||
{
|
||||
assign( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( const Buffer &buffer )
|
||||
{
|
||||
append( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( unsigned int count )
|
||||
{
|
||||
expand( count );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator-=( unsigned int count )
|
||||
{
|
||||
consume( count );
|
||||
return( *this );
|
||||
}
|
||||
operator unsigned char *() const
|
||||
{
|
||||
return( mHead );
|
||||
}
|
||||
operator char *() const
|
||||
{
|
||||
return( (char *)mHead );
|
||||
}
|
||||
unsigned char *operator+(int offset) const
|
||||
{
|
||||
return( (unsigned char *)(mHead+offset) );
|
||||
}
|
||||
unsigned char operator[](int index) const
|
||||
{
|
||||
return( *(mHead+index) );
|
||||
}
|
||||
operator int () const
|
||||
{
|
||||
return( (int)mSize );
|
||||
}
|
||||
int read_into( int sd, unsigned int bytes );
|
||||
Buffer &operator=( const Buffer &buffer )
|
||||
{
|
||||
assign( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( const Buffer &buffer )
|
||||
{
|
||||
append( buffer );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator+=( unsigned int count )
|
||||
{
|
||||
expand( count );
|
||||
return( *this );
|
||||
}
|
||||
Buffer &operator-=( unsigned int count )
|
||||
{
|
||||
consume( count );
|
||||
return( *this );
|
||||
}
|
||||
operator unsigned char *() const
|
||||
{
|
||||
return( mHead );
|
||||
}
|
||||
operator char *() const
|
||||
{
|
||||
return( (char *)mHead );
|
||||
}
|
||||
unsigned char *operator+(int offset) const
|
||||
{
|
||||
return( (unsigned char *)(mHead+offset) );
|
||||
}
|
||||
unsigned char operator[](int index) const
|
||||
{
|
||||
return( *(mHead+index) );
|
||||
}
|
||||
operator int () const
|
||||
{
|
||||
return( (int)mSize );
|
||||
}
|
||||
int read_into( int sd, unsigned int bytes );
|
||||
};
|
||||
|
||||
#endif // ZM_BUFFER_H
|
||||
|
|
|
@ -21,29 +21,29 @@
|
|||
#include "zm_camera.h"
|
||||
|
||||
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
id( p_id ),
|
||||
type( p_type ),
|
||||
width( p_width),
|
||||
height( p_height ),
|
||||
colours( p_colours ),
|
||||
subpixelorder( p_subpixelorder ),
|
||||
brightness( p_brightness ),
|
||||
hue( p_hue ),
|
||||
colour( p_colour ),
|
||||
contrast( p_contrast ),
|
||||
capture( p_capture )
|
||||
id( p_id ),
|
||||
type( p_type ),
|
||||
width( p_width),
|
||||
height( p_height ),
|
||||
colours( p_colours ),
|
||||
subpixelorder( p_subpixelorder ),
|
||||
brightness( p_brightness ),
|
||||
hue( p_hue ),
|
||||
colour( p_colour ),
|
||||
contrast( p_contrast ),
|
||||
capture( p_capture )
|
||||
{
|
||||
pixels = width * height;
|
||||
imagesize = pixels * colours;
|
||||
pixels = width * height;
|
||||
imagesize = pixels * colours;
|
||||
|
||||
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
|
||||
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
|
||||
|
||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
||||
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
|
||||
Fatal("Image size is not multiples of 16");
|
||||
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
|
||||
Fatal("Image size is not multiples of 12 and 16");
|
||||
}
|
||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
||||
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
|
||||
Fatal("Image size is not multiples of 16");
|
||||
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
|
||||
Fatal("Image size is not multiples of 12 and 16");
|
||||
}
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
|
|
|
@ -32,52 +32,52 @@
|
|||
class Camera
|
||||
{
|
||||
protected:
|
||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
|
||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
|
||||
|
||||
int id;
|
||||
SourceType type;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int colours;
|
||||
unsigned int subpixelorder;
|
||||
unsigned int pixels;
|
||||
unsigned int imagesize;
|
||||
int brightness;
|
||||
int hue;
|
||||
int colour;
|
||||
int contrast;
|
||||
bool capture;
|
||||
int id;
|
||||
SourceType type;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned int colours;
|
||||
unsigned int subpixelorder;
|
||||
unsigned int pixels;
|
||||
unsigned int imagesize;
|
||||
int brightness;
|
||||
int hue;
|
||||
int colour;
|
||||
int contrast;
|
||||
bool capture;
|
||||
|
||||
public:
|
||||
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
virtual ~Camera();
|
||||
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
virtual ~Camera();
|
||||
|
||||
int getId() const { return( id ); }
|
||||
SourceType Type() const { return( type ); }
|
||||
bool IsLocal() const { return( type == LOCAL_SRC ); }
|
||||
bool IsRemote() const { return( type == REMOTE_SRC ); }
|
||||
bool IsFile() const { return( type == FILE_SRC ); }
|
||||
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
||||
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
||||
bool IscURL() const { return( type == CURL_SRC ); }
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( colours ); }
|
||||
unsigned int SubpixelOrder() const { return( subpixelorder ); }
|
||||
unsigned int Pixels() const { return( pixels ); }
|
||||
unsigned int ImageSize() const { return( imagesize ); }
|
||||
int getId() const { return( id ); }
|
||||
SourceType Type() const { return( type ); }
|
||||
bool IsLocal() const { return( type == LOCAL_SRC ); }
|
||||
bool IsRemote() const { return( type == REMOTE_SRC ); }
|
||||
bool IsFile() const { return( type == FILE_SRC ); }
|
||||
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
||||
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
||||
bool IscURL() const { return( type == CURL_SRC ); }
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( colours ); }
|
||||
unsigned int SubpixelOrder() const { return( subpixelorder ); }
|
||||
unsigned int Pixels() const { return( pixels ); }
|
||||
unsigned int ImageSize() const { return( imagesize ); }
|
||||
|
||||
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
|
||||
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
|
||||
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
|
||||
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
|
||||
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
|
||||
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
|
||||
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
|
||||
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
|
||||
|
||||
bool CanCapture() const { return( capture ); }
|
||||
bool CanCapture() const { return( capture ); }
|
||||
|
||||
virtual int PrimeCapture() { return( 0 ); }
|
||||
virtual int PreCapture()=0;
|
||||
virtual int Capture( Image &image )=0;
|
||||
virtual int PostCapture()=0;
|
||||
virtual int PrimeCapture() { return( 0 ); }
|
||||
virtual int PreCapture()=0;
|
||||
virtual int Capture( Image &image )=0;
|
||||
virtual int PostCapture()=0;
|
||||
};
|
||||
|
||||
#endif // ZM_CAMERA_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
|
@ -29,277 +29,277 @@
|
|||
|
||||
void zmLoadConfig()
|
||||
{
|
||||
FILE *cfg;
|
||||
char line[512];
|
||||
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
|
||||
{
|
||||
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
|
||||
}
|
||||
while ( fgets( line, sizeof(line), cfg ) != NULL )
|
||||
{
|
||||
char *line_ptr = line;
|
||||
FILE *cfg;
|
||||
char line[512];
|
||||
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
|
||||
{
|
||||
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
|
||||
}
|
||||
while ( fgets( line, sizeof(line), cfg ) != NULL )
|
||||
{
|
||||
char *line_ptr = line;
|
||||
|
||||
// Trim off any cr/lf line endings
|
||||
int chomp_len = strcspn( line_ptr, "\r\n" );
|
||||
line_ptr[chomp_len] = '\0';
|
||||
// Trim off any cr/lf line endings
|
||||
int chomp_len = strcspn( line_ptr, "\r\n" );
|
||||
line_ptr[chomp_len] = '\0';
|
||||
|
||||
// Remove leading white space
|
||||
int white_len = strspn( line_ptr, " \t" );
|
||||
line_ptr += white_len;
|
||||
// Remove leading white space
|
||||
int white_len = strspn( line_ptr, " \t" );
|
||||
line_ptr += white_len;
|
||||
|
||||
// Check for comment or empty line
|
||||
if ( *line_ptr == '\0' || *line_ptr == '#' )
|
||||
continue;
|
||||
// Check for comment or empty line
|
||||
if ( *line_ptr == '\0' || *line_ptr == '#' )
|
||||
continue;
|
||||
|
||||
// Remove trailing white space
|
||||
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
|
||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
|
||||
{
|
||||
*temp_ptr-- = '\0';
|
||||
temp_ptr--;
|
||||
}
|
||||
// Remove trailing white space
|
||||
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
|
||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
|
||||
{
|
||||
*temp_ptr-- = '\0';
|
||||
temp_ptr--;
|
||||
}
|
||||
|
||||
// Now look for the '=' in the middle of the line
|
||||
temp_ptr = strchr( line_ptr, '=' );
|
||||
if ( !temp_ptr )
|
||||
{
|
||||
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
|
||||
continue;
|
||||
}
|
||||
// Now look for the '=' in the middle of the line
|
||||
temp_ptr = strchr( line_ptr, '=' );
|
||||
if ( !temp_ptr )
|
||||
{
|
||||
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Assign the name and value parts
|
||||
char *name_ptr = line_ptr;
|
||||
char *val_ptr = temp_ptr+1;
|
||||
// Assign the name and value parts
|
||||
char *name_ptr = line_ptr;
|
||||
char *val_ptr = temp_ptr+1;
|
||||
|
||||
// Trim trailing space from the name part
|
||||
do
|
||||
{
|
||||
*temp_ptr = '\0';
|
||||
temp_ptr--;
|
||||
}
|
||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
|
||||
// Trim trailing space from the name part
|
||||
do
|
||||
{
|
||||
*temp_ptr = '\0';
|
||||
temp_ptr--;
|
||||
}
|
||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
|
||||
|
||||
// Remove leading white space from the value part
|
||||
white_len = strspn( val_ptr, " \t" );
|
||||
val_ptr += white_len;
|
||||
// Remove leading white space from the value part
|
||||
white_len = strspn( val_ptr, " \t" );
|
||||
val_ptr += white_len;
|
||||
|
||||
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
|
||||
staticConfig.DB_HOST = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
|
||||
staticConfig.DB_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
|
||||
staticConfig.DB_USER = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||
staticConfig.DB_PASS = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
|
||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
|
||||
staticConfig.SERVER_ID = atoi(val_ptr);
|
||||
else
|
||||
{
|
||||
// We ignore this now as there may be more parameters than the
|
||||
// c/c++ binaries are bothered about
|
||||
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
|
||||
}
|
||||
} // end foreach line of the config
|
||||
fclose( cfg );
|
||||
zmDbConnect();
|
||||
config.Load();
|
||||
config.Assign();
|
||||
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
|
||||
staticConfig.DB_HOST = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
|
||||
staticConfig.DB_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
|
||||
staticConfig.DB_USER = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||
staticConfig.DB_PASS = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
|
||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
|
||||
staticConfig.SERVER_ID = atoi(val_ptr);
|
||||
else
|
||||
{
|
||||
// We ignore this now as there may be more parameters than the
|
||||
// c/c++ binaries are bothered about
|
||||
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
|
||||
}
|
||||
} // end foreach line of the config
|
||||
fclose( cfg );
|
||||
zmDbConnect();
|
||||
config.Load();
|
||||
config.Assign();
|
||||
|
||||
// Populate the server config entries
|
||||
if ( ! staticConfig.SERVER_ID ) {
|
||||
if ( ! staticConfig.SERVER_NAME.empty() ) {
|
||||
// Populate the server config entries
|
||||
if ( ! staticConfig.SERVER_ID ) {
|
||||
if ( ! staticConfig.SERVER_NAME.empty() ) {
|
||||
|
||||
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
|
||||
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_ID = atoi(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
|
||||
}
|
||||
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
|
||||
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_ID = atoi(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
|
||||
}
|
||||
|
||||
} // end if has SERVER_NAME
|
||||
} else if ( staticConfig.SERVER_NAME.empty() ) {
|
||||
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
|
||||
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
|
||||
} // end if has SERVER_NAME
|
||||
} else if ( staticConfig.SERVER_NAME.empty() ) {
|
||||
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
|
||||
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
|
||||
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_NAME = std::string(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
|
||||
}
|
||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||
staticConfig.SERVER_NAME = std::string(dbrow[0]);
|
||||
} else {
|
||||
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
|
||||
}
|
||||
|
||||
}
|
||||
if ( ! staticConfig.SERVER_ID ) {
|
||||
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." );
|
||||
} else {
|
||||
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
|
||||
}
|
||||
}
|
||||
if ( ! staticConfig.SERVER_ID ) {
|
||||
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." );
|
||||
} else {
|
||||
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
|
||||
}
|
||||
}
|
||||
|
||||
StaticConfig staticConfig;
|
||||
|
||||
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type )
|
||||
{
|
||||
name = new char[strlen(p_name)+1];
|
||||
strcpy( name, p_name );
|
||||
value = new char[strlen(p_value)+1];
|
||||
strcpy( value, p_value );
|
||||
type = new char[strlen(p_type)+1];
|
||||
strcpy( type, p_type );
|
||||
name = new char[strlen(p_name)+1];
|
||||
strcpy( name, p_name );
|
||||
value = new char[strlen(p_value)+1];
|
||||
strcpy( value, p_value );
|
||||
type = new char[strlen(p_type)+1];
|
||||
strcpy( type, p_type );
|
||||
|
||||
//Info( "Created new config item %s = %s (%s)\n", name, value, type );
|
||||
//Info( "Created new config item %s = %s (%s)\n", name, value, type );
|
||||
|
||||
accessed = false;
|
||||
accessed = false;
|
||||
}
|
||||
|
||||
ConfigItem::~ConfigItem()
|
||||
{
|
||||
delete[] name;
|
||||
delete[] value;
|
||||
delete[] type;
|
||||
delete[] name;
|
||||
delete[] value;
|
||||
delete[] type;
|
||||
}
|
||||
|
||||
void ConfigItem::ConvertValue() const
|
||||
{
|
||||
if ( !strcmp( type, "boolean" ) )
|
||||
{
|
||||
cfg_type = CFG_BOOLEAN;
|
||||
cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
|
||||
}
|
||||
else if ( !strcmp( type, "integer" ) )
|
||||
{
|
||||
cfg_type = CFG_INTEGER;
|
||||
cfg_value.integer_value = strtol( value, 0, 10 );
|
||||
}
|
||||
else if ( !strcmp( type, "hexadecimal" ) )
|
||||
{
|
||||
cfg_type = CFG_INTEGER;
|
||||
cfg_value.integer_value = strtol( value, 0, 16 );
|
||||
}
|
||||
else if ( !strcmp( type, "decimal" ) )
|
||||
{
|
||||
cfg_type = CFG_DECIMAL;
|
||||
cfg_value.decimal_value = strtod( value, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg_type = CFG_STRING;
|
||||
cfg_value.string_value = value;
|
||||
}
|
||||
accessed = true;
|
||||
if ( !strcmp( type, "boolean" ) )
|
||||
{
|
||||
cfg_type = CFG_BOOLEAN;
|
||||
cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
|
||||
}
|
||||
else if ( !strcmp( type, "integer" ) )
|
||||
{
|
||||
cfg_type = CFG_INTEGER;
|
||||
cfg_value.integer_value = strtol( value, 0, 10 );
|
||||
}
|
||||
else if ( !strcmp( type, "hexadecimal" ) )
|
||||
{
|
||||
cfg_type = CFG_INTEGER;
|
||||
cfg_value.integer_value = strtol( value, 0, 16 );
|
||||
}
|
||||
else if ( !strcmp( type, "decimal" ) )
|
||||
{
|
||||
cfg_type = CFG_DECIMAL;
|
||||
cfg_value.decimal_value = strtod( value, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg_type = CFG_STRING;
|
||||
cfg_value.string_value = value;
|
||||
}
|
||||
accessed = true;
|
||||
}
|
||||
|
||||
bool ConfigItem::BooleanValue() const
|
||||
{
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
|
||||
if ( cfg_type != CFG_BOOLEAN )
|
||||
{
|
||||
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( cfg_type != CFG_BOOLEAN )
|
||||
{
|
||||
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return( cfg_value.boolean_value );
|
||||
return( cfg_value.boolean_value );
|
||||
}
|
||||
|
||||
int ConfigItem::IntegerValue() const
|
||||
{
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
|
||||
if ( cfg_type != CFG_INTEGER )
|
||||
{
|
||||
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( cfg_type != CFG_INTEGER )
|
||||
{
|
||||
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return( cfg_value.integer_value );
|
||||
return( cfg_value.integer_value );
|
||||
}
|
||||
|
||||
double ConfigItem::DecimalValue() const
|
||||
{
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
|
||||
if ( cfg_type != CFG_DECIMAL )
|
||||
{
|
||||
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( cfg_type != CFG_DECIMAL )
|
||||
{
|
||||
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return( cfg_value.decimal_value );
|
||||
return( cfg_value.decimal_value );
|
||||
}
|
||||
|
||||
const char *ConfigItem::StringValue() const
|
||||
{
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
if ( !accessed )
|
||||
ConvertValue();
|
||||
|
||||
if ( cfg_type != CFG_STRING )
|
||||
{
|
||||
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( cfg_type != CFG_STRING )
|
||||
{
|
||||
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return( cfg_value.string_value );
|
||||
return( cfg_value.string_value );
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
n_items = 0;
|
||||
items = 0;
|
||||
n_items = 0;
|
||||
items = 0;
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
if ( items )
|
||||
{
|
||||
for ( int i = 0; i < n_items; i++ )
|
||||
{
|
||||
delete items[i];
|
||||
}
|
||||
delete[] items;
|
||||
}
|
||||
if ( items )
|
||||
{
|
||||
for ( int i = 0; i < n_items; i++ )
|
||||
{
|
||||
delete items[i];
|
||||
}
|
||||
delete[] items;
|
||||
}
|
||||
}
|
||||
|
||||
void Config::Load()
|
||||
{
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
|
||||
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
n_items = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
n_items = mysql_num_rows( result );
|
||||
|
||||
if ( n_items <= ZM_MAX_CFG_ID )
|
||||
{
|
||||
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( n_items <= ZM_MAX_CFG_ID )
|
||||
{
|
||||
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
items = new ConfigItem *[n_items];
|
||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
|
||||
{
|
||||
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
|
||||
}
|
||||
mysql_free_result( result );
|
||||
items = new ConfigItem *[n_items];
|
||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
|
||||
{
|
||||
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
|
||||
}
|
||||
mysql_free_result( result );
|
||||
}
|
||||
|
||||
void Config::Assign()
|
||||
|
@ -309,27 +309,27 @@ ZM_CFG_ASSIGN_LIST
|
|||
|
||||
const ConfigItem &Config::Item( int id )
|
||||
{
|
||||
if ( !n_items )
|
||||
{
|
||||
Load();
|
||||
Assign();
|
||||
}
|
||||
if ( !n_items )
|
||||
{
|
||||
Load();
|
||||
Assign();
|
||||
}
|
||||
|
||||
if ( id < 0 || id > ZM_MAX_CFG_ID )
|
||||
{
|
||||
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( id < 0 || id > ZM_MAX_CFG_ID )
|
||||
{
|
||||
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
ConfigItem *item = items[id];
|
||||
ConfigItem *item = items[id];
|
||||
|
||||
if ( !item )
|
||||
{
|
||||
Error( "Can't find config item %d", id );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( !item )
|
||||
{
|
||||
Error( "Can't find config item %d", id );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return( *item );
|
||||
return( *item );
|
||||
}
|
||||
|
||||
Config config;
|
||||
|
|
|
@ -25,48 +25,48 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||
|
||||
#ifdef HAVE_LIBAVFORMAT
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#define ZM_HAS_FFMPEG 1
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||
|
||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||
|
||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||
|
||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||
|
||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||
|
||||
extern void zmLoadConfig();
|
||||
|
||||
struct StaticConfig
|
||||
{
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
std::string DB_HOST;
|
||||
std::string DB_NAME;
|
||||
std::string DB_USER;
|
||||
std::string DB_PASS;
|
||||
std::string PATH_WEB;
|
||||
std::string SERVER_NAME;
|
||||
unsigned int SERVER_ID;
|
||||
};
|
||||
|
||||
extern StaticConfig staticConfig;
|
||||
|
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
|
|||
class ConfigItem
|
||||
{
|
||||
private:
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
char *name;
|
||||
char *value;
|
||||
char *type;
|
||||
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||
mutable union
|
||||
{
|
||||
bool boolean_value;
|
||||
int integer_value;
|
||||
double decimal_value;
|
||||
char *string_value;
|
||||
} cfg_value;
|
||||
mutable bool accessed;
|
||||
|
||||
public:
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||
~ConfigItem();
|
||||
void ConvertValue() const;
|
||||
bool BooleanValue() const;
|
||||
int IntegerValue() const;
|
||||
double DecimalValue() const;
|
||||
const char *StringValue() const;
|
||||
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
inline operator bool() const
|
||||
{
|
||||
return( BooleanValue() );
|
||||
}
|
||||
inline operator int() const
|
||||
{
|
||||
return( IntegerValue() );
|
||||
}
|
||||
inline operator double() const
|
||||
{
|
||||
return( DecimalValue() );
|
||||
}
|
||||
inline operator const char *() const
|
||||
{
|
||||
return( StringValue() );
|
||||
}
|
||||
};
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
ZM_CFG_DECLARE_LIST
|
||||
ZM_CFG_DECLARE_LIST
|
||||
|
||||
private:
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
int n_items;
|
||||
ConfigItem **items;
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
void Load();
|
||||
void Assign();
|
||||
const ConfigItem &Item( int id );
|
||||
};
|
||||
|
||||
extern Config config;
|
||||
|
|
|
@ -28,40 +28,40 @@
|
|||
class Coord
|
||||
{
|
||||
private:
|
||||
int x, y;
|
||||
int x, y;
|
||||
|
||||
public:
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||
{
|
||||
}
|
||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||
{
|
||||
}
|
||||
inline int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
inline Coord() : x(0), y(0)
|
||||
{
|
||||
}
|
||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||
{
|
||||
}
|
||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||
{
|
||||
}
|
||||
inline int &X() { return( x ); }
|
||||
inline const int &X() const { return( x ); }
|
||||
inline int &Y() { return( y ); }
|
||||
inline const int &Y() const { return( y ); }
|
||||
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||
{
|
||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||
return( result );
|
||||
}
|
||||
|
||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||
|
||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||
};
|
||||
|
||||
#endif // ZM_COORD_H
|
||||
|
|
|
@ -31,523 +31,523 @@ size_t content_length_match_len;
|
|||
size_t content_type_match_len;
|
||||
|
||||
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
||||
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
||||
{
|
||||
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
cURLCamera::~cURLCamera()
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
|
||||
Terminate();
|
||||
}
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void cURLCamera::Initialise()
|
||||
{
|
||||
content_length_match_len = strlen(content_length_match);
|
||||
content_type_match_len = strlen(content_type_match);
|
||||
content_length_match_len = strlen(content_length_match);
|
||||
content_type_match_len = strlen(content_type_match);
|
||||
|
||||
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
|
||||
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
|
||||
|
||||
/* cURL initialization */
|
||||
cRet = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if(cRet != CURLE_OK) {
|
||||
Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet));
|
||||
}
|
||||
/* cURL initialization */
|
||||
cRet = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if(cRet != CURLE_OK) {
|
||||
Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet));
|
||||
}
|
||||
|
||||
Debug(2,"libcurl version: %s",curl_version());
|
||||
Debug(2,"libcurl version: %s",curl_version());
|
||||
|
||||
/* Create the shared data mutex */
|
||||
nRet = pthread_mutex_init(&shareddata_mutex, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Shared data mutex creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the data available condition variable */
|
||||
nRet = pthread_cond_init(&data_available_cond, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Data available condition variable creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the request complete condition variable */
|
||||
nRet = pthread_cond_init(&request_complete_cond, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Request complete condition variable creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the shared data mutex */
|
||||
nRet = pthread_mutex_init(&shareddata_mutex, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Shared data mutex creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the data available condition variable */
|
||||
nRet = pthread_cond_init(&data_available_cond, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Data available condition variable creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the request complete condition variable */
|
||||
nRet = pthread_cond_init(&request_complete_cond, NULL);
|
||||
if(nRet != 0) {
|
||||
Fatal("Request complete condition variable creation failed: %s",strerror(nRet));
|
||||
}
|
||||
|
||||
/* Create the thread */
|
||||
nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this);
|
||||
if(nRet != 0) {
|
||||
Fatal("Thread creation failed: %s",strerror(nRet));
|
||||
}
|
||||
/* Create the thread */
|
||||
nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this);
|
||||
if(nRet != 0) {
|
||||
Fatal("Thread creation failed: %s",strerror(nRet));
|
||||
}
|
||||
}
|
||||
|
||||
void cURLCamera::Terminate()
|
||||
{
|
||||
/* Signal the thread to terminate */
|
||||
bTerminate = true;
|
||||
/* Signal the thread to terminate */
|
||||
bTerminate = true;
|
||||
|
||||
/* Wait for thread termination */
|
||||
pthread_join(thread, NULL);
|
||||
/* Wait for thread termination */
|
||||
pthread_join(thread, NULL);
|
||||
|
||||
/* Destroy condition variables */
|
||||
pthread_cond_destroy(&request_complete_cond);
|
||||
pthread_cond_destroy(&data_available_cond);
|
||||
/* Destroy condition variables */
|
||||
pthread_cond_destroy(&request_complete_cond);
|
||||
pthread_cond_destroy(&data_available_cond);
|
||||
|
||||
/* Destroy mutex */
|
||||
pthread_mutex_destroy(&shareddata_mutex);
|
||||
/* Destroy mutex */
|
||||
pthread_mutex_destroy(&shareddata_mutex);
|
||||
|
||||
/* cURL cleanup */
|
||||
curl_global_cleanup();
|
||||
/* cURL cleanup */
|
||||
curl_global_cleanup();
|
||||
|
||||
}
|
||||
|
||||
int cURLCamera::PrimeCapture()
|
||||
{
|
||||
//Info( "Priming capture from %s", mPath.c_str() );
|
||||
return 0;
|
||||
//Info( "Priming capture from %s", mPath.c_str() );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cURLCamera::PreCapture()
|
||||
{
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int cURLCamera::Capture( Image &image )
|
||||
{
|
||||
bool frameComplete = false;
|
||||
bool frameComplete = false;
|
||||
|
||||
/* MODE_STREAM specific variables */
|
||||
bool SubHeadersParsingComplete = false;
|
||||
unsigned int frame_content_length = 0;
|
||||
std::string frame_content_type;
|
||||
bool need_more_data = false;
|
||||
/* MODE_STREAM specific variables */
|
||||
bool SubHeadersParsingComplete = false;
|
||||
unsigned int frame_content_length = 0;
|
||||
std::string frame_content_type;
|
||||
bool need_more_data = false;
|
||||
|
||||
/* Grab the mutex to ensure exclusive access to the shared data */
|
||||
lock();
|
||||
/* Grab the mutex to ensure exclusive access to the shared data */
|
||||
lock();
|
||||
|
||||
while (!frameComplete) {
|
||||
while (!frameComplete) {
|
||||
|
||||
/* If the work thread did a reset, reset our local variables */
|
||||
if(bReset) {
|
||||
SubHeadersParsingComplete = false;
|
||||
frame_content_length = 0;
|
||||
frame_content_type.clear();
|
||||
need_more_data = false;
|
||||
bReset = false;
|
||||
}
|
||||
/* If the work thread did a reset, reset our local variables */
|
||||
if(bReset) {
|
||||
SubHeadersParsingComplete = false;
|
||||
frame_content_length = 0;
|
||||
frame_content_type.clear();
|
||||
need_more_data = false;
|
||||
bReset = false;
|
||||
}
|
||||
|
||||
if(mode == MODE_UNSET) {
|
||||
/* Don't have a mode yet. Sleep while waiting for data */
|
||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||
return -20;
|
||||
}
|
||||
}
|
||||
if(mode == MODE_UNSET) {
|
||||
/* Don't have a mode yet. Sleep while waiting for data */
|
||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||
return -20;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == MODE_STREAM) {
|
||||
if(mode == MODE_STREAM) {
|
||||
|
||||
/* Subheader parsing */
|
||||
while(!SubHeadersParsingComplete && !need_more_data) {
|
||||
/* Subheader parsing */
|
||||
while(!SubHeadersParsingComplete && !need_more_data) {
|
||||
|
||||
size_t crlf_start, crlf_end, crlf_size;
|
||||
std::string subheader;
|
||||
size_t crlf_start, crlf_end, crlf_size;
|
||||
std::string subheader;
|
||||
|
||||
/* Check if the buffer contains something */
|
||||
if(databuffer.empty()) {
|
||||
/* Empty buffer, wait for data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
/* Check if the buffer contains something */
|
||||
if(databuffer.empty()) {
|
||||
/* Empty buffer, wait for data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find crlf start */
|
||||
crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
|
||||
if(crlf_start == databuffer.size()) {
|
||||
/* Not found, wait for more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
/* Find crlf start */
|
||||
crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
|
||||
if(crlf_start == databuffer.size()) {
|
||||
/* Not found, wait for more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* See if we have enough data for determining crlf length */
|
||||
if(databuffer.size() < crlf_start+5) {
|
||||
/* Need more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
/* See if we have enough data for determining crlf length */
|
||||
if(databuffer.size() < crlf_start+5) {
|
||||
/* Need more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Find crlf end and calculate crlf size */
|
||||
crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
|
||||
crlf_size = (crlf_start + crlf_end) - crlf_start;
|
||||
/* Find crlf end and calculate crlf size */
|
||||
crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
|
||||
crlf_size = (crlf_start + crlf_end) - crlf_start;
|
||||
|
||||
/* Is this the end of a previous stream? (This is just before the boundary) */
|
||||
if(crlf_start == 0) {
|
||||
databuffer.consume(crlf_size);
|
||||
continue;
|
||||
}
|
||||
/* Is this the end of a previous stream? (This is just before the boundary) */
|
||||
if(crlf_start == 0) {
|
||||
databuffer.consume(crlf_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for invalid CRLF size */
|
||||
if(crlf_size > 4) {
|
||||
Error("Invalid CRLF length");
|
||||
}
|
||||
/* Check for invalid CRLF size */
|
||||
if(crlf_size > 4) {
|
||||
Error("Invalid CRLF length");
|
||||
}
|
||||
|
||||
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
|
||||
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
|
||||
/* This is the last header */
|
||||
SubHeadersParsingComplete = true;
|
||||
}
|
||||
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
|
||||
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
|
||||
/* This is the last header */
|
||||
SubHeadersParsingComplete = true;
|
||||
}
|
||||
|
||||
/* Copy the subheader, excluding the crlf */
|
||||
subheader.assign(databuffer, crlf_start);
|
||||
/* Copy the subheader, excluding the crlf */
|
||||
subheader.assign(databuffer, crlf_start);
|
||||
|
||||
/* Advance the buffer past this one */
|
||||
databuffer.consume(crlf_start+crlf_size);
|
||||
/* Advance the buffer past this one */
|
||||
databuffer.consume(crlf_start+crlf_size);
|
||||
|
||||
Debug(7,"Got subheader: %s",subheader.c_str());
|
||||
Debug(7,"Got subheader: %s",subheader.c_str());
|
||||
|
||||
/* Find where the data in this header starts */
|
||||
size_t subheader_data_start = subheader.rfind(' ');
|
||||
if(subheader_data_start == std::string::npos) {
|
||||
subheader_data_start = subheader.find(':');
|
||||
}
|
||||
/* Find where the data in this header starts */
|
||||
size_t subheader_data_start = subheader.rfind(' ');
|
||||
if(subheader_data_start == std::string::npos) {
|
||||
subheader_data_start = subheader.find(':');
|
||||
}
|
||||
|
||||
/* Extract the data into a string */
|
||||
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
|
||||
/* Extract the data into a string */
|
||||
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
|
||||
|
||||
Debug(8,"Got subheader data: %s",subheader_data.c_str());
|
||||
Debug(8,"Got subheader data: %s",subheader_data.c_str());
|
||||
|
||||
/* Check the header */
|
||||
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
|
||||
/* Found the content-length header */
|
||||
frame_content_length = atoi(subheader_data.c_str());
|
||||
Debug(6,"Got content-length subheader: %d",frame_content_length);
|
||||
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
/* Found the content-type header */
|
||||
frame_content_type = subheader_data;
|
||||
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
|
||||
}
|
||||
/* Check the header */
|
||||
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
|
||||
/* Found the content-length header */
|
||||
frame_content_length = atoi(subheader_data.c_str());
|
||||
Debug(6,"Got content-length subheader: %d",frame_content_length);
|
||||
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
/* Found the content-type header */
|
||||
frame_content_type = subheader_data;
|
||||
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to extract the frame */
|
||||
if(!need_more_data) {
|
||||
if(!SubHeadersParsingComplete) {
|
||||
/* We haven't parsed all headers yet */
|
||||
need_more_data = true;
|
||||
} else if(frame_content_length <= 0) {
|
||||
/* Invalid frame */
|
||||
Error("Invalid frame: invalid content length");
|
||||
} else if(frame_content_type != "image/jpeg") {
|
||||
/* Unsupported frame type */
|
||||
Error("Unsupported frame: %s",frame_content_type.c_str());
|
||||
} else if(frame_content_length > databuffer.size()) {
|
||||
/* Incomplete frame, wait for more data */
|
||||
need_more_data = true;
|
||||
} else {
|
||||
/* All good. decode the image */
|
||||
image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
|
||||
frameComplete = true;
|
||||
}
|
||||
}
|
||||
/* Attempt to extract the frame */
|
||||
if(!need_more_data) {
|
||||
if(!SubHeadersParsingComplete) {
|
||||
/* We haven't parsed all headers yet */
|
||||
need_more_data = true;
|
||||
} else if(frame_content_length <= 0) {
|
||||
/* Invalid frame */
|
||||
Error("Invalid frame: invalid content length");
|
||||
} else if(frame_content_type != "image/jpeg") {
|
||||
/* Unsupported frame type */
|
||||
Error("Unsupported frame: %s",frame_content_type.c_str());
|
||||
} else if(frame_content_length > databuffer.size()) {
|
||||
/* Incomplete frame, wait for more data */
|
||||
need_more_data = true;
|
||||
} else {
|
||||
/* All good. decode the image */
|
||||
image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
|
||||
frameComplete = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt to get more data */
|
||||
if(need_more_data) {
|
||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||
return -18;
|
||||
}
|
||||
need_more_data = false;
|
||||
}
|
||||
/* Attempt to get more data */
|
||||
if(need_more_data) {
|
||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||
return -18;
|
||||
}
|
||||
need_more_data = false;
|
||||
}
|
||||
|
||||
} else if(mode == MODE_SINGLE) {
|
||||
/* Check if we have anything */
|
||||
if (!single_offsets.empty()) {
|
||||
if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
|
||||
/* Extract frame */
|
||||
image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
|
||||
single_offsets.pop_front();
|
||||
frameComplete = true;
|
||||
} else {
|
||||
/* This shouldn't happen */
|
||||
Error("Internal error. Attempting recovery");
|
||||
databuffer.consume(single_offsets.front());
|
||||
single_offsets.pop_front();
|
||||
}
|
||||
} else {
|
||||
/* Don't have a frame yet, wait for the request complete condition variable */
|
||||
nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
|
||||
return -19;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Failed to match content-type */
|
||||
Fatal("Unable to match Content-Type. Check URL, username and password");
|
||||
} /* mode */
|
||||
} else if(mode == MODE_SINGLE) {
|
||||
/* Check if we have anything */
|
||||
if (!single_offsets.empty()) {
|
||||
if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
|
||||
/* Extract frame */
|
||||
image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
|
||||
single_offsets.pop_front();
|
||||
frameComplete = true;
|
||||
} else {
|
||||
/* This shouldn't happen */
|
||||
Error("Internal error. Attempting recovery");
|
||||
databuffer.consume(single_offsets.front());
|
||||
single_offsets.pop_front();
|
||||
}
|
||||
} else {
|
||||
/* Don't have a frame yet, wait for the request complete condition variable */
|
||||
nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
|
||||
return -19;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Failed to match content-type */
|
||||
Fatal("Unable to match Content-Type. Check URL, username and password");
|
||||
} /* mode */
|
||||
|
||||
} /* frameComplete loop */
|
||||
} /* frameComplete loop */
|
||||
|
||||
/* Release the mutex */
|
||||
unlock();
|
||||
/* Release the mutex */
|
||||
unlock();
|
||||
|
||||
if(!frameComplete)
|
||||
return -1;
|
||||
if(!frameComplete)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cURLCamera::PostCapture()
|
||||
{
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
lock();
|
||||
lock();
|
||||
|
||||
/* Append the data we just received to our buffer */
|
||||
databuffer.append((const char*)buffer, size*nmemb);
|
||||
/* Append the data we just received to our buffer */
|
||||
databuffer.append((const char*)buffer, size*nmemb);
|
||||
|
||||
/* Signal data available */
|
||||
nRet = pthread_cond_signal(&data_available_cond);
|
||||
if(nRet != 0) {
|
||||
Error("Failed signaling data available condition variable: %s",strerror(nRet));
|
||||
return -16;
|
||||
}
|
||||
/* Signal data available */
|
||||
nRet = pthread_cond_signal(&data_available_cond);
|
||||
if(nRet != 0) {
|
||||
Error("Failed signaling data available condition variable: %s",strerror(nRet));
|
||||
return -16;
|
||||
}
|
||||
|
||||
unlock();
|
||||
unlock();
|
||||
|
||||
/* Return bytes processed */
|
||||
return size*nmemb;
|
||||
/* Return bytes processed */
|
||||
return size*nmemb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
std::string header;
|
||||
header.assign((const char*)buffer, size*nmemb);
|
||||
std::string header;
|
||||
header.assign((const char*)buffer, size*nmemb);
|
||||
|
||||
Debug(4,"Got header: %s",header.c_str());
|
||||
Debug(4,"Got header: %s",header.c_str());
|
||||
|
||||
/* Check Content-Type header */
|
||||
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
size_t pos = header.find(';');
|
||||
if(pos != std::string::npos) {
|
||||
header.erase(pos, std::string::npos);
|
||||
}
|
||||
/* Check Content-Type header */
|
||||
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
size_t pos = header.find(';');
|
||||
if(pos != std::string::npos) {
|
||||
header.erase(pos, std::string::npos);
|
||||
}
|
||||
|
||||
pos = header.rfind(' ');
|
||||
if(pos == std::string::npos) {
|
||||
pos = header.find(':');
|
||||
}
|
||||
pos = header.rfind(' ');
|
||||
if(pos == std::string::npos) {
|
||||
pos = header.find(':');
|
||||
}
|
||||
|
||||
std::string content_type = header.substr(pos+1, std::string::npos);
|
||||
Debug(6,"Content-Type is: %s",content_type.c_str());
|
||||
std::string content_type = header.substr(pos+1, std::string::npos);
|
||||
Debug(6,"Content-Type is: %s",content_type.c_str());
|
||||
|
||||
lock();
|
||||
lock();
|
||||
|
||||
const char* multipart_match = "multipart/x-mixed-replace";
|
||||
const char* image_jpeg_match = "image/jpeg";
|
||||
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
||||
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
||||
mode = MODE_STREAM;
|
||||
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
||||
Debug(7,"Content type matched as image/jpeg");
|
||||
mode = MODE_SINGLE;
|
||||
}
|
||||
const char* multipart_match = "multipart/x-mixed-replace";
|
||||
const char* image_jpeg_match = "image/jpeg";
|
||||
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
||||
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
||||
mode = MODE_STREAM;
|
||||
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
||||
Debug(7,"Content type matched as image/jpeg");
|
||||
mode = MODE_SINGLE;
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
/* Return bytes processed */
|
||||
return size*nmemb;
|
||||
/* Return bytes processed */
|
||||
return size*nmemb;
|
||||
}
|
||||
|
||||
void* cURLCamera::thread_func()
|
||||
{
|
||||
long tRet;
|
||||
double dSize;
|
||||
long tRet;
|
||||
double dSize;
|
||||
|
||||
c = curl_easy_init();
|
||||
if(c == NULL) {
|
||||
Fatal("Failed getting easy handle from libcurl");
|
||||
}
|
||||
c = curl_easy_init();
|
||||
if(c == NULL) {
|
||||
Fatal("Failed getting easy handle from libcurl");
|
||||
}
|
||||
|
||||
/* Set URL */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet));
|
||||
/* Set URL */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet));
|
||||
|
||||
/* Header callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet));
|
||||
/* Header callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet));
|
||||
|
||||
/* Data callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet));
|
||||
/* Data callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet));
|
||||
|
||||
/* Progress callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet));
|
||||
/* Progress callback */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this);
|
||||
if(cRet != CURLE_OK)
|
||||
Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet));
|
||||
|
||||
/* Set username and password */
|
||||
if(!mUser.empty()) {
|
||||
cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Error("Failed setting username: %s", curl_easy_strerror(cRet));
|
||||
}
|
||||
if(!mPass.empty()) {
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Error("Failed setting password: %s", curl_easy_strerror(cRet));
|
||||
}
|
||||
/* Set username and password */
|
||||
if(!mUser.empty()) {
|
||||
cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Error("Failed setting username: %s", curl_easy_strerror(cRet));
|
||||
}
|
||||
if(!mPass.empty()) {
|
||||
cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
Error("Failed setting password: %s", curl_easy_strerror(cRet));
|
||||
}
|
||||
|
||||
/* Authenication preference */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
||||
if(cRet != CURLE_OK)
|
||||
Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet));
|
||||
/* Authenication preference */
|
||||
cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
||||
if(cRet != CURLE_OK)
|
||||
Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet));
|
||||
|
||||
|
||||
/* Work loop */
|
||||
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
||||
tRet = 0;
|
||||
while(!bTerminate) {
|
||||
/* Do the work */
|
||||
cRet = curl_easy_perform(c);
|
||||
/* Work loop */
|
||||
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
||||
tRet = 0;
|
||||
while(!bTerminate) {
|
||||
/* Do the work */
|
||||
cRet = curl_easy_perform(c);
|
||||
|
||||
if(mode == MODE_SINGLE) {
|
||||
if(cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* Attempt to get the size of the file */
|
||||
cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
|
||||
if(cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* We need to lock for the offsets array and the condition variable */
|
||||
lock();
|
||||
/* Push the size into our offsets array */
|
||||
if(dSize > 0) {
|
||||
single_offsets.push_back(dSize);
|
||||
} else {
|
||||
Fatal("Unable to get the size of the image");
|
||||
}
|
||||
/* Signal the request complete condition variable */
|
||||
tRet = pthread_cond_signal(&request_complete_cond);
|
||||
if(tRet != 0) {
|
||||
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
|
||||
}
|
||||
/* Unlock */
|
||||
unlock();
|
||||
if(mode == MODE_SINGLE) {
|
||||
if(cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* Attempt to get the size of the file */
|
||||
cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
|
||||
if(cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* We need to lock for the offsets array and the condition variable */
|
||||
lock();
|
||||
/* Push the size into our offsets array */
|
||||
if(dSize > 0) {
|
||||
single_offsets.push_back(dSize);
|
||||
} else {
|
||||
Fatal("Unable to get the size of the image");
|
||||
}
|
||||
/* Signal the request complete condition variable */
|
||||
tRet = pthread_cond_signal(&request_complete_cond);
|
||||
if(tRet != 0) {
|
||||
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
|
||||
}
|
||||
/* Unlock */
|
||||
unlock();
|
||||
|
||||
} else if (mode == MODE_STREAM) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (mode == MODE_STREAM) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return value checking */
|
||||
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
||||
/* Aborted */
|
||||
break;
|
||||
} else if (cRet != CURLE_OK) {
|
||||
/* Some error */
|
||||
Error("cURL Request failed: %s",curl_easy_strerror(cRet));
|
||||
if(attempt < CURL_MAXRETRY) {
|
||||
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
|
||||
/* Do a reset */
|
||||
lock();
|
||||
databuffer.clear();
|
||||
single_offsets.clear();
|
||||
mode = MODE_UNSET;
|
||||
bReset = true;
|
||||
unlock();
|
||||
}
|
||||
tRet = -50;
|
||||
}
|
||||
}
|
||||
/* Return value checking */
|
||||
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
||||
/* Aborted */
|
||||
break;
|
||||
} else if (cRet != CURLE_OK) {
|
||||
/* Some error */
|
||||
Error("cURL Request failed: %s",curl_easy_strerror(cRet));
|
||||
if(attempt < CURL_MAXRETRY) {
|
||||
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
|
||||
/* Do a reset */
|
||||
lock();
|
||||
databuffer.clear();
|
||||
single_offsets.clear();
|
||||
mode = MODE_UNSET;
|
||||
bReset = true;
|
||||
unlock();
|
||||
}
|
||||
tRet = -50;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
curl_easy_cleanup(c);
|
||||
c = NULL;
|
||||
/* Cleanup */
|
||||
curl_easy_cleanup(c);
|
||||
c = NULL;
|
||||
|
||||
return (void*)tRet;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
int cURLCamera::lock() {
|
||||
int nRet;
|
||||
int nRet;
|
||||
|
||||
/* Lock shared data */
|
||||
nRet = pthread_mutex_lock(&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed locking shared data mutex: %s",strerror(nRet));
|
||||
}
|
||||
return nRet;
|
||||
/* Lock shared data */
|
||||
nRet = pthread_mutex_lock(&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed locking shared data mutex: %s",strerror(nRet));
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
int cURLCamera::unlock() {
|
||||
int nRet;
|
||||
int nRet;
|
||||
|
||||
/* Unlock shared data */
|
||||
nRet = pthread_mutex_unlock(&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed unlocking shared data mutex: %s",strerror(nRet));
|
||||
}
|
||||
return nRet;
|
||||
/* Unlock shared data */
|
||||
nRet = pthread_mutex_unlock(&shareddata_mutex);
|
||||
if(nRet != 0) {
|
||||
Error("Failed unlocking shared data mutex: %s",strerror(nRet));
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
/* Signal the curl thread to terminate */
|
||||
if(bTerminate)
|
||||
return -10;
|
||||
/* Signal the curl thread to terminate */
|
||||
if(bTerminate)
|
||||
return -10;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* These functions call the functions in the class for the correct object */
|
||||
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
|
||||
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
|
||||
}
|
||||
|
||||
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
|
||||
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
|
||||
}
|
||||
|
||||
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
|
||||
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
|
||||
}
|
||||
|
||||
void* thread_func_dispatcher(void* object) {
|
||||
return ((cURLCamera*)object)->thread_func();
|
||||
return ((cURLCamera*)object)->thread_func();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,55 +42,55 @@
|
|||
class cURLCamera : public Camera
|
||||
{
|
||||
protected:
|
||||
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
|
||||
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
|
||||
|
||||
std::string mPath;
|
||||
std::string mUser;
|
||||
std::string mPass;
|
||||
std::string mPath;
|
||||
std::string mUser;
|
||||
std::string mPass;
|
||||
|
||||
/* cURL object(s) */
|
||||
CURL* c;
|
||||
/* cURL object(s) */
|
||||
CURL* c;
|
||||
|
||||
/* Shared data */
|
||||
volatile bool bTerminate;
|
||||
volatile bool bReset;
|
||||
volatile mode_t mode;
|
||||
Buffer databuffer;
|
||||
std::deque<size_t> single_offsets;
|
||||
/* Shared data */
|
||||
volatile bool bTerminate;
|
||||
volatile bool bReset;
|
||||
volatile mode_t mode;
|
||||
Buffer databuffer;
|
||||
std::deque<size_t> single_offsets;
|
||||
|
||||
/* pthread objects */
|
||||
pthread_t thread;
|
||||
pthread_mutex_t shareddata_mutex;
|
||||
pthread_cond_t data_available_cond;
|
||||
pthread_cond_t request_complete_cond;
|
||||
/* pthread objects */
|
||||
pthread_t thread;
|
||||
pthread_mutex_t shareddata_mutex;
|
||||
pthread_cond_t data_available_cond;
|
||||
pthread_cond_t request_complete_cond;
|
||||
|
||||
public:
|
||||
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~cURLCamera();
|
||||
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~cURLCamera();
|
||||
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Username() const { return( mUser ); }
|
||||
const std::string &Password() const { return( mPass ); }
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Username() const { return( mUser ); }
|
||||
const std::string &Password() const { return( mPass ); }
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
|
||||
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||
int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data);
|
||||
void* thread_func();
|
||||
int lock();
|
||||
int unlock();
|
||||
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||
int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data);
|
||||
void* thread_func();
|
||||
int lock();
|
||||
int unlock();
|
||||
|
||||
private:
|
||||
int nRet;
|
||||
CURLcode cRet;
|
||||
int nRet;
|
||||
CURLcode cRet;
|
||||
|
||||
};
|
||||
|
||||
|
|
130
src/zm_db.cpp
130
src/zm_db.cpp
|
@ -29,85 +29,85 @@ int zmDbConnected = false;
|
|||
|
||||
void zmDbConnect()
|
||||
{
|
||||
if ( !mysql_init( &dbconn ) )
|
||||
{
|
||||
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
my_bool reconnect = 1;
|
||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
||||
if ( colonIndex != std::string::npos )
|
||||
if ( !mysql_init( &dbconn ) )
|
||||
{
|
||||
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
my_bool reconnect = 1;
|
||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
||||
if ( colonIndex != std::string::npos )
|
||||
{
|
||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
||||
{
|
||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
||||
{
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
||||
{
|
||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
||||
{
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
||||
{
|
||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
zmDbConnected = true;
|
||||
}
|
||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
||||
{
|
||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
zmDbConnected = true;
|
||||
}
|
||||
|
||||
void zmDbClose()
|
||||
{
|
||||
if ( zmDbConnected )
|
||||
{
|
||||
mysql_close( &dbconn );
|
||||
// mysql_init() call implicitly mysql_library_init() but
|
||||
// mysql_close() does not call mysql_library_end()
|
||||
mysql_library_end();
|
||||
zmDbConnected = false;
|
||||
}
|
||||
if ( zmDbConnected )
|
||||
{
|
||||
mysql_close( &dbconn );
|
||||
// mysql_init() call implicitly mysql_library_init() but
|
||||
// mysql_close() does not call mysql_library_end()
|
||||
mysql_library_end();
|
||||
zmDbConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
MYSQL_RES * zmDbFetch( const char * query ) {
|
||||
if ( ! zmDbConnected ) {
|
||||
Error( "Not connected." );
|
||||
return NULL;
|
||||
}
|
||||
if ( ! zmDbConnected ) {
|
||||
Error( "Not connected." );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( mysql_query( &dbconn, query ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
Debug( 4, "Success running query: %s", query );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
if ( mysql_query( &dbconn, query ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
Debug( 4, "Success running query: %s", query );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
} // end MYSQL_RES * zmDbFetch( const char * query );
|
||||
|
||||
MYSQL_ROW zmDbFetchOne( const char *query ) {
|
||||
MYSQL_RES *result = zmDbFetch( query );
|
||||
int n_rows = mysql_num_rows( result );
|
||||
if ( n_rows != 1 ) {
|
||||
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
||||
return NULL;
|
||||
}
|
||||
MYSQL_RES *result = zmDbFetch( query );
|
||||
int n_rows = mysql_num_rows( result );
|
||||
if ( n_rows != 1 ) {
|
||||
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
mysql_free_result( result );
|
||||
if ( ! dbrow ) {
|
||||
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
return dbrow;
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
mysql_free_result( result );
|
||||
if ( ! dbrow ) {
|
||||
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
||||
return NULL;
|
||||
}
|
||||
return dbrow;
|
||||
}
|
||||
|
|
2442
src/zm_event.cpp
2442
src/zm_event.cpp
File diff suppressed because it is too large
Load Diff
316
src/zm_event.h
316
src/zm_event.h
|
@ -41,7 +41,7 @@
|
|||
class Zone;
|
||||
class Monitor;
|
||||
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
|
||||
//
|
||||
// Class describing events, i.e. captured periods of activity.
|
||||
|
@ -51,219 +51,219 @@ class Event
|
|||
friend class EventStream;
|
||||
|
||||
protected:
|
||||
static bool initialised;
|
||||
static char capture_file_format[PATH_MAX];
|
||||
static char analyse_file_format[PATH_MAX];
|
||||
static char general_file_format[PATH_MAX];
|
||||
static bool initialised;
|
||||
static char capture_file_format[PATH_MAX];
|
||||
static char analyse_file_format[PATH_MAX];
|
||||
static char general_file_format[PATH_MAX];
|
||||
|
||||
protected:
|
||||
static int sd;
|
||||
static int sd;
|
||||
|
||||
public:
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
typedef std::set<std::string> StringSet;
|
||||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
|
||||
protected:
|
||||
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
||||
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
||||
|
||||
struct PreAlarmData
|
||||
{
|
||||
Image *image;
|
||||
struct timeval timestamp;
|
||||
unsigned int score;
|
||||
Image *alarm_frame;
|
||||
};
|
||||
struct PreAlarmData
|
||||
{
|
||||
Image *image;
|
||||
struct timeval timestamp;
|
||||
unsigned int score;
|
||||
Image *alarm_frame;
|
||||
};
|
||||
|
||||
static int pre_alarm_count;
|
||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||
static int pre_alarm_count;
|
||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||
|
||||
protected:
|
||||
unsigned int id;
|
||||
Monitor *monitor;
|
||||
struct timeval start_time;
|
||||
struct timeval end_time;
|
||||
std::string cause;
|
||||
StringSetMap noteSetMap;
|
||||
int frames;
|
||||
int alarm_frames;
|
||||
unsigned int tot_score;
|
||||
unsigned int max_score;
|
||||
char path[PATH_MAX];
|
||||
unsigned int id;
|
||||
Monitor *monitor;
|
||||
struct timeval start_time;
|
||||
struct timeval end_time;
|
||||
std::string cause;
|
||||
StringSetMap noteSetMap;
|
||||
int frames;
|
||||
int alarm_frames;
|
||||
unsigned int tot_score;
|
||||
unsigned int max_score;
|
||||
char path[PATH_MAX];
|
||||
|
||||
protected:
|
||||
int last_db_frame;
|
||||
int last_db_frame;
|
||||
|
||||
protected:
|
||||
static void Initialise()
|
||||
{
|
||||
if ( initialised )
|
||||
return;
|
||||
static void Initialise()
|
||||
{
|
||||
if ( initialised )
|
||||
return;
|
||||
|
||||
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
|
||||
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
|
||||
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
|
||||
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
|
||||
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
|
||||
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
|
||||
|
||||
initialised = true;
|
||||
}
|
||||
initialised = true;
|
||||
}
|
||||
|
||||
void createNotes( std::string ¬es );
|
||||
void createNotes( std::string ¬es );
|
||||
|
||||
public:
|
||||
static bool OpenFrameSocket( int );
|
||||
static bool ValidateFrameSocket( int );
|
||||
static bool OpenFrameSocket( int );
|
||||
static bool ValidateFrameSocket( int );
|
||||
|
||||
public:
|
||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
|
||||
~Event();
|
||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
|
||||
~Event();
|
||||
|
||||
int Id() const { return( id ); }
|
||||
const std::string &Cause() { return( cause ); }
|
||||
int Frames() const { return( frames ); }
|
||||
int AlarmFrames() const { return( alarm_frames ); }
|
||||
int Id() const { return( id ); }
|
||||
const std::string &Cause() { return( cause ); }
|
||||
int Frames() const { return( frames ); }
|
||||
int AlarmFrames() const { return( alarm_frames ); }
|
||||
|
||||
const struct timeval &StartTime() const { return( start_time ); }
|
||||
const struct timeval &EndTime() const { return( end_time ); }
|
||||
struct timeval &EndTime() { return( end_time ); }
|
||||
const struct timeval &StartTime() const { return( start_time ); }
|
||||
const struct timeval &EndTime() const { return( end_time ); }
|
||||
struct timeval &EndTime() { return( end_time ); }
|
||||
|
||||
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
||||
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
|
||||
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
||||
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
|
||||
|
||||
void updateNotes( const StringSetMap &stringSetMap );
|
||||
void updateNotes( const StringSetMap &stringSetMap );
|
||||
|
||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
||||
|
||||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time )
|
||||
{
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
return( subpath );
|
||||
}
|
||||
static const char *getSubPath( time_t *time )
|
||||
{
|
||||
return( Event::getSubPath( localtime( time ) ) );
|
||||
}
|
||||
static const char *getSubPath( struct tm *time )
|
||||
{
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
return( subpath );
|
||||
}
|
||||
static const char *getSubPath( time_t *time )
|
||||
{
|
||||
return( Event::getSubPath( localtime( time ) ) );
|
||||
}
|
||||
|
||||
public:
|
||||
static int PreAlarmCount()
|
||||
{
|
||||
return( pre_alarm_count );
|
||||
}
|
||||
static void EmptyPreAlarmFrames()
|
||||
{
|
||||
if ( pre_alarm_count > 0 )
|
||||
{
|
||||
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
||||
{
|
||||
delete pre_alarm_data[i].image;
|
||||
delete pre_alarm_data[i].alarm_frame;
|
||||
}
|
||||
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
||||
}
|
||||
pre_alarm_count = 0;
|
||||
}
|
||||
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||
pre_alarm_data[pre_alarm_count].score = score;
|
||||
if ( alarm_frame )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
||||
}
|
||||
pre_alarm_count++;
|
||||
}
|
||||
void SavePreAlarmFrames()
|
||||
{
|
||||
for ( int i = 0; i < pre_alarm_count; i++ )
|
||||
{
|
||||
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
||||
}
|
||||
EmptyPreAlarmFrames();
|
||||
}
|
||||
static int PreAlarmCount()
|
||||
{
|
||||
return( pre_alarm_count );
|
||||
}
|
||||
static void EmptyPreAlarmFrames()
|
||||
{
|
||||
if ( pre_alarm_count > 0 )
|
||||
{
|
||||
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
||||
{
|
||||
delete pre_alarm_data[i].image;
|
||||
delete pre_alarm_data[i].alarm_frame;
|
||||
}
|
||||
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
||||
}
|
||||
pre_alarm_count = 0;
|
||||
}
|
||||
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||
pre_alarm_data[pre_alarm_count].score = score;
|
||||
if ( alarm_frame )
|
||||
{
|
||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
||||
}
|
||||
pre_alarm_count++;
|
||||
}
|
||||
void SavePreAlarmFrames()
|
||||
{
|
||||
for ( int i = 0; i < pre_alarm_count; i++ )
|
||||
{
|
||||
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
||||
}
|
||||
EmptyPreAlarmFrames();
|
||||
}
|
||||
};
|
||||
|
||||
class EventStream : public StreamBase
|
||||
{
|
||||
public:
|
||||
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
||||
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
||||
|
||||
protected:
|
||||
struct FrameData {
|
||||
//unsigned long id;
|
||||
time_t timestamp;
|
||||
time_t offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
};
|
||||
struct FrameData {
|
||||
//unsigned long id;
|
||||
time_t timestamp;
|
||||
time_t offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
};
|
||||
|
||||
struct EventData
|
||||
{
|
||||
unsigned long event_id;
|
||||
unsigned long monitor_id;
|
||||
unsigned long frame_count;
|
||||
time_t start_time;
|
||||
double duration;
|
||||
char path[PATH_MAX];
|
||||
int n_frames;
|
||||
FrameData *frames;
|
||||
};
|
||||
struct EventData
|
||||
{
|
||||
unsigned long event_id;
|
||||
unsigned long monitor_id;
|
||||
unsigned long frame_count;
|
||||
time_t start_time;
|
||||
double duration;
|
||||
char path[PATH_MAX];
|
||||
int n_frames;
|
||||
FrameData *frames;
|
||||
};
|
||||
|
||||
protected:
|
||||
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||
|
||||
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||
|
||||
protected:
|
||||
StreamMode mode;
|
||||
bool forceEventChange;
|
||||
StreamMode mode;
|
||||
bool forceEventChange;
|
||||
|
||||
protected:
|
||||
int curr_frame_id;
|
||||
double curr_stream_time;
|
||||
int curr_frame_id;
|
||||
double curr_stream_time;
|
||||
|
||||
EventData *event_data;
|
||||
EventData *event_data;
|
||||
|
||||
protected:
|
||||
bool loadEventData( int event_id );
|
||||
bool loadInitialEventData( int init_event_id, int init_frame_id );
|
||||
bool loadInitialEventData( int monitor_id, time_t event_time );
|
||||
bool loadEventData( int event_id );
|
||||
bool loadInitialEventData( int init_event_id, int init_frame_id );
|
||||
bool loadInitialEventData( int monitor_id, time_t event_time );
|
||||
|
||||
void checkEventLoaded();
|
||||
void processCommand( const CmdMsg *msg );
|
||||
bool sendFrame( int delta_us );
|
||||
void checkEventLoaded();
|
||||
void processCommand( const CmdMsg *msg );
|
||||
bool sendFrame( int delta_us );
|
||||
|
||||
public:
|
||||
EventStream()
|
||||
{
|
||||
mode = DEFAULT_MODE;
|
||||
EventStream()
|
||||
{
|
||||
mode = DEFAULT_MODE;
|
||||
|
||||
forceEventChange = false;
|
||||
forceEventChange = false;
|
||||
|
||||
curr_frame_id = 0;
|
||||
curr_stream_time = 0.0;
|
||||
curr_frame_id = 0;
|
||||
curr_stream_time = 0.0;
|
||||
|
||||
event_data = 0;
|
||||
}
|
||||
void setStreamStart( int init_event_id, int init_frame_id=0 )
|
||||
{
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
loadMonitor( event_data->monitor_id );
|
||||
}
|
||||
void setStreamStart( int monitor_id, time_t event_time )
|
||||
{
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
loadMonitor( monitor_id );
|
||||
}
|
||||
void setStreamMode( StreamMode p_mode )
|
||||
{
|
||||
mode = p_mode;
|
||||
}
|
||||
void runStream();
|
||||
event_data = 0;
|
||||
}
|
||||
void setStreamStart( int init_event_id, int init_frame_id=0 )
|
||||
{
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
loadMonitor( event_data->monitor_id );
|
||||
}
|
||||
void setStreamStart( int monitor_id, time_t event_time )
|
||||
{
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
loadMonitor( monitor_id );
|
||||
}
|
||||
void setStreamMode( StreamMode p_mode )
|
||||
{
|
||||
mode = p_mode;
|
||||
}
|
||||
void runStream();
|
||||
};
|
||||
|
||||
#endif // ZM_EVENT_H
|
||||
|
|
|
@ -27,42 +27,42 @@
|
|||
class Exception
|
||||
{
|
||||
protected:
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||
|
||||
protected:
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
std::string mMessage;
|
||||
Severity mSeverity;
|
||||
|
||||
public:
|
||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||
{
|
||||
}
|
||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
const std::string &getMessage() const
|
||||
{
|
||||
return( mMessage );
|
||||
}
|
||||
Severity getSeverity() const
|
||||
{
|
||||
return( mSeverity );
|
||||
}
|
||||
bool isInfo() const
|
||||
{
|
||||
return( mSeverity == INFO );
|
||||
}
|
||||
bool isWarning() const
|
||||
{
|
||||
return( mSeverity == WARNING );
|
||||
}
|
||||
bool isError() const
|
||||
{
|
||||
return( mSeverity == ERROR );
|
||||
}
|
||||
bool isFatal() const
|
||||
{
|
||||
return( mSeverity == FATAL );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_EXCEPTION_H
|
||||
|
|
|
@ -25,210 +25,210 @@
|
|||
|
||||
#if HAVE_LIBAVUTIL
|
||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
||||
enum _AVPIXELFORMAT pf;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
|
||||
Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder);
|
||||
Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder);
|
||||
|
||||
switch(p_colours) {
|
||||
case ZM_COLOUR_RGB24:
|
||||
{
|
||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
/* BGR subpixel order */
|
||||
pf = AV_PIX_FMT_BGR24;
|
||||
} else {
|
||||
/* Assume RGB subpixel order */
|
||||
pf = AV_PIX_FMT_RGB24;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZM_COLOUR_RGB32:
|
||||
{
|
||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
/* ARGB subpixel order */
|
||||
pf = AV_PIX_FMT_ARGB;
|
||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
/* ABGR subpixel order */
|
||||
pf = AV_PIX_FMT_ABGR;
|
||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
/* BGRA subpixel order */
|
||||
pf = AV_PIX_FMT_BGRA;
|
||||
} else {
|
||||
/* Assume RGBA subpixel order */
|
||||
pf = AV_PIX_FMT_RGBA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZM_COLOUR_GRAY8:
|
||||
pf = AV_PIX_FMT_GRAY8;
|
||||
break;
|
||||
default:
|
||||
Panic("Unexpected colours: %d",p_colours);
|
||||
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
|
||||
break;
|
||||
}
|
||||
switch(p_colours) {
|
||||
case ZM_COLOUR_RGB24:
|
||||
{
|
||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
/* BGR subpixel order */
|
||||
pf = AV_PIX_FMT_BGR24;
|
||||
} else {
|
||||
/* Assume RGB subpixel order */
|
||||
pf = AV_PIX_FMT_RGB24;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZM_COLOUR_RGB32:
|
||||
{
|
||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
/* ARGB subpixel order */
|
||||
pf = AV_PIX_FMT_ARGB;
|
||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
/* ABGR subpixel order */
|
||||
pf = AV_PIX_FMT_ABGR;
|
||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
/* BGRA subpixel order */
|
||||
pf = AV_PIX_FMT_BGRA;
|
||||
} else {
|
||||
/* Assume RGBA subpixel order */
|
||||
pf = AV_PIX_FMT_RGBA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZM_COLOUR_GRAY8:
|
||||
pf = AV_PIX_FMT_GRAY8;
|
||||
break;
|
||||
default:
|
||||
Panic("Unexpected colours: %d",p_colours);
|
||||
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
|
||||
break;
|
||||
}
|
||||
|
||||
return pf;
|
||||
return pf;
|
||||
}
|
||||
#endif // HAVE_LIBAVUTIL
|
||||
|
||||
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||
SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) {
|
||||
Debug(4,"SWScale object created");
|
||||
Debug(4,"SWScale object created");
|
||||
|
||||
/* Allocate AVFrame for the input */
|
||||
/* Allocate AVFrame for the input */
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
input_avframe = av_frame_alloc();
|
||||
input_avframe = av_frame_alloc();
|
||||
#else
|
||||
input_avframe = avcodec_alloc_frame();
|
||||
input_avframe = avcodec_alloc_frame();
|
||||
#endif
|
||||
if(input_avframe == NULL) {
|
||||
Fatal("Failed allocating AVFrame for the input");
|
||||
}
|
||||
if(input_avframe == NULL) {
|
||||
Fatal("Failed allocating AVFrame for the input");
|
||||
}
|
||||
|
||||
/* Allocate AVFrame for the output */
|
||||
/* Allocate AVFrame for the output */
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
output_avframe = av_frame_alloc();
|
||||
output_avframe = av_frame_alloc();
|
||||
#else
|
||||
output_avframe = avcodec_alloc_frame();
|
||||
output_avframe = avcodec_alloc_frame();
|
||||
#endif
|
||||
if(output_avframe == NULL) {
|
||||
Fatal("Failed allocating AVFrame for the output");
|
||||
}
|
||||
if(output_avframe == NULL) {
|
||||
Fatal("Failed allocating AVFrame for the output");
|
||||
}
|
||||
}
|
||||
|
||||
SWScale::~SWScale() {
|
||||
|
||||
/* Free up everything */
|
||||
/* Free up everything */
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
av_frame_free( &input_avframe );
|
||||
av_frame_free( &input_avframe );
|
||||
#else
|
||||
av_freep( &input_avframe );
|
||||
av_freep( &input_avframe );
|
||||
#endif
|
||||
//input_avframe = NULL;
|
||||
//input_avframe = NULL;
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
av_frame_free( &output_avframe );
|
||||
av_frame_free( &output_avframe );
|
||||
#else
|
||||
av_freep( &output_avframe );
|
||||
av_freep( &output_avframe );
|
||||
#endif
|
||||
//output_avframe = NULL;
|
||||
//output_avframe = NULL;
|
||||
|
||||
if(swscale_ctx) {
|
||||
sws_freeContext(swscale_ctx);
|
||||
swscale_ctx = NULL;
|
||||
}
|
||||
if(swscale_ctx) {
|
||||
sws_freeContext(swscale_ctx);
|
||||
swscale_ctx = NULL;
|
||||
}
|
||||
|
||||
Debug(4,"SWScale object destroyed");
|
||||
Debug(4,"SWScale object destroyed");
|
||||
}
|
||||
|
||||
int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||
|
||||
/* Assign the defaults */
|
||||
default_input_pf = in_pf;
|
||||
default_output_pf = out_pf;
|
||||
default_width = width;
|
||||
default_height = height;
|
||||
/* Assign the defaults */
|
||||
default_input_pf = in_pf;
|
||||
default_output_pf = out_pf;
|
||||
default_width = width;
|
||||
default_height = height;
|
||||
|
||||
gotdefaults = true;
|
||||
gotdefaults = true;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||
/* Parameter checking */
|
||||
if(in_buffer == NULL || out_buffer == NULL) {
|
||||
Error("NULL Input or output buffer");
|
||||
return -1;
|
||||
}
|
||||
if(in_pf == 0 || out_pf == 0) {
|
||||
Error("Invalid input or output pixel formats");
|
||||
return -2;
|
||||
}
|
||||
if(!width || !height) {
|
||||
Error("Invalid width or height");
|
||||
return -3;
|
||||
}
|
||||
/* Parameter checking */
|
||||
if(in_buffer == NULL || out_buffer == NULL) {
|
||||
Error("NULL Input or output buffer");
|
||||
return -1;
|
||||
}
|
||||
if(in_pf == 0 || out_pf == 0) {
|
||||
Error("Invalid input or output pixel formats");
|
||||
return -2;
|
||||
}
|
||||
if(!width || !height) {
|
||||
Error("Invalid width or height");
|
||||
return -3;
|
||||
}
|
||||
|
||||
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
||||
/* Warn if the input or output pixelformat is not supported */
|
||||
if(!sws_isSupportedInput(in_pf)) {
|
||||
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
||||
}
|
||||
if(!sws_isSupportedOutput(out_pf)) {
|
||||
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
||||
}
|
||||
/* Warn if the input or output pixelformat is not supported */
|
||||
if(!sws_isSupportedInput(in_pf)) {
|
||||
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
||||
}
|
||||
if(!sws_isSupportedOutput(out_pf)) {
|
||||
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check the buffer sizes */
|
||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||
if(insize != in_buffer_size) {
|
||||
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
||||
return -4;
|
||||
}
|
||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||
if(outsize < out_buffer_size) {
|
||||
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
|
||||
return -5;
|
||||
}
|
||||
/* Check the buffer sizes */
|
||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||
if(insize != in_buffer_size) {
|
||||
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
||||
return -4;
|
||||
}
|
||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||
if(outsize < out_buffer_size) {
|
||||
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Get the context */
|
||||
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
|
||||
if(swscale_ctx == NULL) {
|
||||
Error("Failed getting swscale context");
|
||||
return -6;
|
||||
}
|
||||
/* Get the context */
|
||||
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
|
||||
if(swscale_ctx == NULL) {
|
||||
Error("Failed getting swscale context");
|
||||
return -6;
|
||||
}
|
||||
|
||||
/* Fill in the buffers */
|
||||
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
|
||||
Error("Failed filling input frame with input buffer");
|
||||
return -7;
|
||||
}
|
||||
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
|
||||
Error("Failed filling output frame with output buffer");
|
||||
return -8;
|
||||
}
|
||||
/* Fill in the buffers */
|
||||
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
|
||||
Error("Failed filling input frame with input buffer");
|
||||
return -7;
|
||||
}
|
||||
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
|
||||
Error("Failed filling output frame with output buffer");
|
||||
return -8;
|
||||
}
|
||||
|
||||
/* Do the conversion */
|
||||
if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) {
|
||||
Error("swscale conversion failed");
|
||||
return -10;
|
||||
}
|
||||
/* Do the conversion */
|
||||
if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) {
|
||||
Error("swscale conversion failed");
|
||||
return -10;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||
if(img->Width() != width) {
|
||||
Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
|
||||
return -12;
|
||||
}
|
||||
if(img->Width() != width) {
|
||||
Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
|
||||
return -12;
|
||||
}
|
||||
|
||||
if(img->Height() != height) {
|
||||
Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
|
||||
return -13;
|
||||
}
|
||||
if(img->Height() != height) {
|
||||
Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
|
||||
return -13;
|
||||
}
|
||||
|
||||
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height);
|
||||
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height);
|
||||
}
|
||||
|
||||
int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) {
|
||||
|
||||
if(!gotdefaults) {
|
||||
Error("Defaults are not set");
|
||||
return -24;
|
||||
}
|
||||
if(!gotdefaults) {
|
||||
Error("Defaults are not set");
|
||||
return -24;
|
||||
}
|
||||
|
||||
return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||
return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||
}
|
||||
|
||||
int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) {
|
||||
|
||||
if(!gotdefaults) {
|
||||
Error("Defaults are not set");
|
||||
return -24;
|
||||
}
|
||||
if(!gotdefaults) {
|
||||
Error("Defaults are not set");
|
||||
return -24;
|
||||
}
|
||||
|
||||
return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||
return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||
}
|
||||
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||
|
||||
|
|
164
src/zm_ffmpeg.h
164
src/zm_ffmpeg.h
|
@ -39,8 +39,8 @@ extern "C" {
|
|||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
|
||||
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
|
||||
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
|
||||
#include <libavutil/opt.h>
|
||||
|
@ -56,52 +56,52 @@ extern "C" {
|
|||
|
||||
#if defined(HAVE_LIBAVUTIL_AVUTIL_H)
|
||||
#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100)
|
||||
#define _AVPIXELFORMAT AVPixelFormat
|
||||
#define _AVPIXELFORMAT AVPixelFormat
|
||||
#else
|
||||
#define _AVPIXELFORMAT PixelFormat
|
||||
#define AV_PIX_FMT_NONE PIX_FMT_NONE
|
||||
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
|
||||
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
|
||||
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
|
||||
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
|
||||
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
|
||||
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
|
||||
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
|
||||
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
|
||||
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
|
||||
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
|
||||
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
|
||||
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
|
||||
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
|
||||
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
|
||||
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
|
||||
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
||||
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
|
||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
|
||||
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
|
||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
|
||||
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
|
||||
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
|
||||
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
|
||||
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
|
||||
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
|
||||
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
|
||||
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
|
||||
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
|
||||
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
|
||||
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
|
||||
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
|
||||
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
|
||||
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
|
||||
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
|
||||
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
|
||||
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
|
||||
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
|
||||
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
|
||||
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
|
||||
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
|
||||
#define _AVPIXELFORMAT PixelFormat
|
||||
#define AV_PIX_FMT_NONE PIX_FMT_NONE
|
||||
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
|
||||
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
|
||||
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
|
||||
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
|
||||
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
|
||||
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
|
||||
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
|
||||
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
|
||||
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
|
||||
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
|
||||
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
|
||||
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
|
||||
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
|
||||
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
|
||||
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
|
||||
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
||||
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
|
||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
|
||||
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
|
||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
|
||||
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
|
||||
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
|
||||
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
|
||||
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
|
||||
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
|
||||
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
|
||||
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
|
||||
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
|
||||
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
|
||||
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
|
||||
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
|
||||
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
|
||||
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
|
||||
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
|
||||
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
|
||||
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
|
||||
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
|
||||
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
|
||||
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
|
||||
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
|
||||
#endif
|
||||
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
|
||||
|
||||
|
@ -116,8 +116,8 @@ extern "C" {
|
|||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
|
||||
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
|
||||
#elif HAVE_FFMPEG_AVCODEC_H
|
||||
#include <ffmpeg/avcodec.h>
|
||||
|
@ -125,9 +125,9 @@ extern "C" {
|
|||
|
||||
#if defined(HAVE_LIBAVCODEC_AVCODEC_H)
|
||||
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
|
||||
#define _AVCODECID AVCodecID
|
||||
#define _AVCODECID AVCodecID
|
||||
#else
|
||||
#define _AVCODECID CodecID
|
||||
#define _AVCODECID CodecID
|
||||
#endif
|
||||
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
|
||||
|
||||
|
@ -141,8 +141,8 @@ extern "C" {
|
|||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
|
||||
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
|
||||
#elif HAVE_FFMPEG_AVFORMAT_H
|
||||
#include <ffmpeg/avformat.h>
|
||||
|
@ -157,8 +157,8 @@ extern "C" {
|
|||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
|
||||
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
|
||||
#elif HAVE_FFMPEG_AVDEVICE_H
|
||||
#include <ffmpeg/avdevice.h>
|
||||
|
@ -173,8 +173,8 @@ extern "C" {
|
|||
* b and c the minor and micro versions of libav
|
||||
* d and e the minor and micro versions of FFmpeg */
|
||||
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
|
||||
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||
|
||||
#elif HAVE_FFMPEG_SWSCALE_H
|
||||
#include <ffmpeg/swscale.h>
|
||||
|
@ -203,23 +203,23 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp
|
|||
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||
class SWScale {
|
||||
public:
|
||||
SWScale();
|
||||
~SWScale();
|
||||
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
SWScale();
|
||||
~SWScale();
|
||||
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||
|
||||
protected:
|
||||
bool gotdefaults;
|
||||
struct SwsContext* swscale_ctx;
|
||||
AVFrame* input_avframe;
|
||||
AVFrame* output_avframe;
|
||||
enum _AVPIXELFORMAT default_input_pf;
|
||||
enum _AVPIXELFORMAT default_output_pf;
|
||||
unsigned int default_width;
|
||||
unsigned int default_height;
|
||||
bool gotdefaults;
|
||||
struct SwsContext* swscale_ctx;
|
||||
AVFrame* input_avframe;
|
||||
AVFrame* output_avframe;
|
||||
enum _AVPIXELFORMAT default_input_pf;
|
||||
enum _AVPIXELFORMAT default_output_pf;
|
||||
unsigned int default_width;
|
||||
unsigned int default_height;
|
||||
};
|
||||
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||
|
||||
|
@ -256,21 +256,21 @@ protected:
|
|||
*/
|
||||
#ifdef __cplusplus
|
||||
|
||||
inline static const std::string av_make_error_string(int errnum)
|
||||
{
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
inline static const std::string av_make_error_string(int errnum)
|
||||
{
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
|
||||
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
#else
|
||||
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
|
||||
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
|
||||
#endif
|
||||
return (std::string)errbuf;
|
||||
}
|
||||
return (std::string)errbuf;
|
||||
}
|
||||
|
||||
#undef av_err2str
|
||||
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
|
||||
#undef av_err2str
|
||||
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )
|
||||
|
|
|
@ -28,71 +28,71 @@
|
|||
#endif
|
||||
|
||||
#ifdef SOLARIS
|
||||
#include <sys/errno.h> // for ESRCH
|
||||
#include <sys/errno.h> // for ESRCH
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ),
|
||||
mMethod( p_method ),
|
||||
mOptions( p_options )
|
||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ),
|
||||
mMethod( p_method ),
|
||||
mOptions( p_options )
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
|
||||
mFormatContext = NULL;
|
||||
mVideoStreamId = -1;
|
||||
mCodecContext = NULL;
|
||||
mCodec = NULL;
|
||||
mRawFrame = NULL;
|
||||
mFrame = NULL;
|
||||
frameCount = 0;
|
||||
mIsOpening = false;
|
||||
mCanCapture = false;
|
||||
mOpenStart = 0;
|
||||
mReopenThread = 0;
|
||||
mFormatContext = NULL;
|
||||
mVideoStreamId = -1;
|
||||
mCodecContext = NULL;
|
||||
mCodec = NULL;
|
||||
mRawFrame = NULL;
|
||||
mFrame = NULL;
|
||||
frameCount = 0;
|
||||
mIsOpening = false;
|
||||
mCanCapture = false;
|
||||
mOpenStart = 0;
|
||||
mReopenThread = 0;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
mConvertContext = NULL;
|
||||
#endif
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FfmpegCamera::~FfmpegCamera()
|
||||
{
|
||||
CloseFfmpeg();
|
||||
CloseFfmpeg();
|
||||
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void FfmpegCamera::Initialise()
|
||||
{
|
||||
if ( logDebugging() )
|
||||
av_log_set_level( AV_LOG_DEBUG );
|
||||
else
|
||||
av_log_set_level( AV_LOG_QUIET );
|
||||
if ( logDebugging() )
|
||||
av_log_set_level( AV_LOG_DEBUG );
|
||||
else
|
||||
av_log_set_level( AV_LOG_QUIET );
|
||||
|
||||
av_register_all();
|
||||
av_register_all();
|
||||
}
|
||||
|
||||
void FfmpegCamera::Terminate()
|
||||
|
@ -101,373 +101,373 @@ void FfmpegCamera::Terminate()
|
|||
|
||||
int FfmpegCamera::PrimeCapture()
|
||||
{
|
||||
Info( "Priming capture from %s", mPath.c_str() );
|
||||
Info( "Priming capture from %s", mPath.c_str() );
|
||||
|
||||
if (OpenFfmpeg() != 0){
|
||||
ReopenFfmpeg();
|
||||
}
|
||||
return 0;
|
||||
if (OpenFfmpeg() != 0){
|
||||
ReopenFfmpeg();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FfmpegCamera::PreCapture()
|
||||
{
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int FfmpegCamera::Capture( Image &image )
|
||||
{
|
||||
if (!mCanCapture){
|
||||
return -1;
|
||||
if (!mCanCapture){
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
||||
if (mReopenThread != 0) {
|
||||
void *retval = 0;
|
||||
int ret;
|
||||
|
||||
ret = pthread_join(mReopenThread, &retval);
|
||||
if (ret != 0){
|
||||
Error("Could not join reopen thread.");
|
||||
}
|
||||
|
||||
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
||||
if (mReopenThread != 0) {
|
||||
void *retval = 0;
|
||||
int ret;
|
||||
Info( "Successfully reopened stream." );
|
||||
mReopenThread = 0;
|
||||
}
|
||||
|
||||
ret = pthread_join(mReopenThread, &retval);
|
||||
if (ret != 0){
|
||||
Error("Could not join reopen thread.");
|
||||
}
|
||||
AVPacket packet;
|
||||
uint8_t* directbuffer;
|
||||
|
||||
Info( "Successfully reopened stream." );
|
||||
mReopenThread = 0;
|
||||
}
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if(directbuffer == NULL) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
AVPacket packet;
|
||||
uint8_t* directbuffer;
|
||||
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if(directbuffer == NULL) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int frameComplete = false;
|
||||
while ( !frameComplete )
|
||||
int frameComplete = false;
|
||||
while ( !frameComplete )
|
||||
{
|
||||
int avResult = av_read_frame( mFormatContext, &packet );
|
||||
if ( avResult < 0 )
|
||||
{
|
||||
int avResult = av_read_frame( mFormatContext, &packet );
|
||||
if ( avResult < 0 )
|
||||
{
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
if (
|
||||
// Check if EOF.
|
||||
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
||||
// Check for Connection failure.
|
||||
(avResult == -110)
|
||||
)
|
||||
{
|
||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
||||
ReopenFfmpeg();
|
||||
}
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
if (
|
||||
// Check if EOF.
|
||||
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
||||
// Check for Connection failure.
|
||||
(avResult == -110)
|
||||
)
|
||||
{
|
||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
||||
ReopenFfmpeg();
|
||||
}
|
||||
|
||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||
return( -1 );
|
||||
}
|
||||
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
||||
if ( packet.stream_index == mVideoStreamId )
|
||||
{
|
||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||
return( -1 );
|
||||
}
|
||||
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
||||
if ( packet.stream_index == mVideoStreamId )
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
||||
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
||||
#else
|
||||
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
|
||||
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
|
||||
#endif
|
||||
Fatal( "Unable to decode frame at frame %d", frameCount );
|
||||
Fatal( "Unable to decode frame at frame %d", frameCount );
|
||||
|
||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||
|
||||
if ( frameComplete )
|
||||
{
|
||||
Debug( 3, "Got frame %d", frameCount );
|
||||
if ( frameComplete )
|
||||
{
|
||||
Debug( 3, "Got frame %d", frameCount );
|
||||
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(mConvertContext == NULL) {
|
||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||
if(mConvertContext == NULL) {
|
||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||
|
||||
if(mConvertContext == NULL)
|
||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||
}
|
||||
|
||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
frameCount++;
|
||||
}
|
||||
}
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||
av_packet_unref( &packet);
|
||||
#else
|
||||
av_free_packet( &packet );
|
||||
#endif
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int FfmpegCamera::PostCapture()
|
||||
{
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int FfmpegCamera::OpenFfmpeg() {
|
||||
|
||||
Debug ( 2, "OpenFfmpeg called." );
|
||||
|
||||
mOpenStart = time(NULL);
|
||||
mIsOpening = true;
|
||||
|
||||
// Open the input, not necessarily a file
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
||||
Debug ( 1, "Calling av_open_input_file" );
|
||||
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 )
|
||||
#else
|
||||
// Handle options
|
||||
AVDictionary *opts = 0;
|
||||
StringVector opVect = split(Options(), ",");
|
||||
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" )
|
||||
opVect.push_back("rtsp_transport=udp_multicast");
|
||||
else if ( Method() == "rtpRtsp" )
|
||||
opVect.push_back("rtsp_transport=tcp");
|
||||
else if ( Method() == "rtpRtspHttp" )
|
||||
opVect.push_back("rtsp_transport=http");
|
||||
|
||||
Debug(2, "Number of Options: %d",opVect.size());
|
||||
for (size_t i=0; i<opVect.size(); i++)
|
||||
{
|
||||
StringVector parts = split(opVect[i],"=");
|
||||
if (parts.size() > 1) {
|
||||
parts[0] = trimSpaces(parts[0]);
|
||||
parts[1] = trimSpaces(parts[1]);
|
||||
if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) {
|
||||
Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() );
|
||||
}
|
||||
}
|
||||
Debug ( 1, "Calling avformat_open_input" );
|
||||
|
||||
mFormatContext = avformat_alloc_context( );
|
||||
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
||||
mFormatContext->interrupt_callback.opaque = this;
|
||||
|
||||
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 )
|
||||
#endif
|
||||
{
|
||||
mIsOpening = false;
|
||||
Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
AVDictionaryEntry *e;
|
||||
if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) {
|
||||
Warning( "Option %s not recognized by ffmpeg", e->key);
|
||||
}
|
||||
|
||||
mIsOpening = false;
|
||||
Debug ( 1, "Opened input" );
|
||||
|
||||
// Locate stream info from avformat_open_input
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
|
||||
Debug ( 1, "Calling av_find_stream_info" );
|
||||
if ( av_find_stream_info( mFormatContext ) < 0 )
|
||||
#else
|
||||
Debug ( 1, "Calling avformat_find_stream_info" );
|
||||
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 )
|
||||
#endif
|
||||
Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) );
|
||||
|
||||
Debug ( 1, "Got stream info" );
|
||||
|
||||
// Find first video stream present
|
||||
mVideoStreamId = -1;
|
||||
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
|
||||
{
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||
#else
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||
#endif
|
||||
{
|
||||
mVideoStreamId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( mVideoStreamId == -1 )
|
||||
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Found video stream" );
|
||||
|
||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||
|
||||
// Try and get the codec from the codec context
|
||||
if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
|
||||
Fatal( "Can't find codec for video stream from %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Found decoder" );
|
||||
|
||||
// Open the codec
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||
Debug ( 1, "Calling avcodec_open" );
|
||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||
#else
|
||||
Debug ( 1, "Calling avcodec_open2" );
|
||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||
#endif
|
||||
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Opened codec" );
|
||||
|
||||
// Allocate space for the native video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mRawFrame = av_frame_alloc();
|
||||
#else
|
||||
mRawFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
// Allocate space for the converted video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mFrame = av_frame_alloc();
|
||||
#else
|
||||
mFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Allocated frames" );
|
||||
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
|
||||
Debug ( 1, "Validated imagesize" );
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
||||
}
|
||||
|
||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
if(mConvertContext == NULL)
|
||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||
}
|
||||
|
||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
mCanCapture = true;
|
||||
frameCount++;
|
||||
}
|
||||
}
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||
av_packet_unref( &packet);
|
||||
#else
|
||||
av_free_packet( &packet );
|
||||
#endif
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
int FfmpegCamera::PostCapture()
|
||||
{
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int FfmpegCamera::OpenFfmpeg() {
|
||||
|
||||
Debug ( 2, "OpenFfmpeg called." );
|
||||
|
||||
mOpenStart = time(NULL);
|
||||
mIsOpening = true;
|
||||
|
||||
// Open the input, not necessarily a file
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
||||
Debug ( 1, "Calling av_open_input_file" );
|
||||
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 )
|
||||
#else
|
||||
// Handle options
|
||||
AVDictionary *opts = 0;
|
||||
StringVector opVect = split(Options(), ",");
|
||||
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" )
|
||||
opVect.push_back("rtsp_transport=udp_multicast");
|
||||
else if ( Method() == "rtpRtsp" )
|
||||
opVect.push_back("rtsp_transport=tcp");
|
||||
else if ( Method() == "rtpRtspHttp" )
|
||||
opVect.push_back("rtsp_transport=http");
|
||||
|
||||
Debug(2, "Number of Options: %d",opVect.size());
|
||||
for (size_t i=0; i<opVect.size(); i++)
|
||||
{
|
||||
StringVector parts = split(opVect[i],"=");
|
||||
if (parts.size() > 1) {
|
||||
parts[0] = trimSpaces(parts[0]);
|
||||
parts[1] = trimSpaces(parts[1]);
|
||||
if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) {
|
||||
Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() );
|
||||
}
|
||||
}
|
||||
Debug ( 1, "Calling avformat_open_input" );
|
||||
|
||||
mFormatContext = avformat_alloc_context( );
|
||||
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
||||
mFormatContext->interrupt_callback.opaque = this;
|
||||
|
||||
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 )
|
||||
#endif
|
||||
{
|
||||
mIsOpening = false;
|
||||
Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
AVDictionaryEntry *e;
|
||||
if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) {
|
||||
Warning( "Option %s not recognized by ffmpeg", e->key);
|
||||
}
|
||||
|
||||
mIsOpening = false;
|
||||
Debug ( 1, "Opened input" );
|
||||
|
||||
// Locate stream info from avformat_open_input
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
|
||||
Debug ( 1, "Calling av_find_stream_info" );
|
||||
if ( av_find_stream_info( mFormatContext ) < 0 )
|
||||
#else
|
||||
Debug ( 1, "Calling avformat_find_stream_info" );
|
||||
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 )
|
||||
#endif
|
||||
Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) );
|
||||
|
||||
Debug ( 1, "Got stream info" );
|
||||
|
||||
// Find first video stream present
|
||||
mVideoStreamId = -1;
|
||||
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
|
||||
{
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||
#else
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||
#endif
|
||||
{
|
||||
mVideoStreamId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( mVideoStreamId == -1 )
|
||||
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Found video stream" );
|
||||
|
||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||
|
||||
// Try and get the codec from the codec context
|
||||
if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
|
||||
Fatal( "Can't find codec for video stream from %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Found decoder" );
|
||||
|
||||
// Open the codec
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||
Debug ( 1, "Calling avcodec_open" );
|
||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||
#else
|
||||
Debug ( 1, "Calling avcodec_open2" );
|
||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||
#endif
|
||||
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Opened codec" );
|
||||
|
||||
// Allocate space for the native video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mRawFrame = av_frame_alloc();
|
||||
#else
|
||||
mRawFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
// Allocate space for the converted video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mFrame = av_frame_alloc();
|
||||
#else
|
||||
mFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
||||
|
||||
Debug ( 1, "Allocated frames" );
|
||||
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
|
||||
Debug ( 1, "Validated imagesize" );
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
||||
}
|
||||
|
||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
}
|
||||
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
mCanCapture = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FfmpegCamera::ReopenFfmpeg() {
|
||||
|
||||
Debug(2, "ReopenFfmpeg called.");
|
||||
Debug(2, "ReopenFfmpeg called.");
|
||||
|
||||
mCanCapture = false;
|
||||
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
||||
// Log a fatal error and exit the process.
|
||||
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
||||
}
|
||||
mCanCapture = false;
|
||||
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
||||
// Log a fatal error and exit the process.
|
||||
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FfmpegCamera::CloseFfmpeg(){
|
||||
|
||||
Debug(2, "CloseFfmpeg called.");
|
||||
Debug(2, "CloseFfmpeg called.");
|
||||
|
||||
mCanCapture = false;
|
||||
mCanCapture = false;
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
av_frame_free( &mFrame );
|
||||
av_frame_free( &mRawFrame );
|
||||
av_frame_free( &mFrame );
|
||||
av_frame_free( &mRawFrame );
|
||||
#else
|
||||
av_freep( &mFrame );
|
||||
av_freep( &mRawFrame );
|
||||
av_freep( &mFrame );
|
||||
av_freep( &mRawFrame );
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if ( mConvertContext )
|
||||
{
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
if ( mConvertContext )
|
||||
{
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( mCodecContext )
|
||||
{
|
||||
avcodec_close( mCodecContext );
|
||||
mCodecContext = NULL; // Freed by av_close_input_file
|
||||
}
|
||||
if ( mFormatContext )
|
||||
{
|
||||
if ( mCodecContext )
|
||||
{
|
||||
avcodec_close( mCodecContext );
|
||||
mCodecContext = NULL; // Freed by av_close_input_file
|
||||
}
|
||||
if ( mFormatContext )
|
||||
{
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
|
||||
av_close_input_file( mFormatContext );
|
||||
av_close_input_file( mFormatContext );
|
||||
#else
|
||||
avformat_close_input( &mFormatContext );
|
||||
avformat_close_input( &mFormatContext );
|
||||
#endif
|
||||
mFormatContext = NULL;
|
||||
}
|
||||
mFormatContext = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
|
||||
{
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
if (camera->mIsOpening){
|
||||
int now = time(NULL);
|
||||
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
|
||||
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||
return 1;
|
||||
}
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
if (camera->mIsOpening){
|
||||
int now = time(NULL);
|
||||
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
|
||||
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||
if (ctx == NULL) return NULL;
|
||||
if (ctx == NULL) return NULL;
|
||||
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
|
||||
while (1){
|
||||
// Close current stream.
|
||||
camera->CloseFfmpeg();
|
||||
while (1){
|
||||
// Close current stream.
|
||||
camera->CloseFfmpeg();
|
||||
|
||||
// Sleep if necessary to not reconnect too fast.
|
||||
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
||||
wait = wait < 0 ? 0 : wait;
|
||||
if (wait > 0){
|
||||
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
||||
sleep(wait);
|
||||
}
|
||||
|
||||
if (camera->OpenFfmpeg() == 0){
|
||||
return NULL;
|
||||
}
|
||||
// Sleep if necessary to not reconnect too fast.
|
||||
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
||||
wait = wait < 0 ? 0 : wait;
|
||||
if (wait > 0){
|
||||
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
||||
sleep(wait);
|
||||
}
|
||||
|
||||
if (camera->OpenFfmpeg() == 0){
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -33,51 +33,51 @@
|
|||
class FfmpegCamera : public Camera
|
||||
{
|
||||
protected:
|
||||
std::string mPath;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
std::string mPath;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
|
||||
int frameCount;
|
||||
int frameCount;
|
||||
|
||||
#if HAVE_LIBAVFORMAT
|
||||
AVFormatContext *mFormatContext;
|
||||
int mVideoStreamId;
|
||||
AVCodecContext *mCodecContext;
|
||||
AVCodec *mCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
AVFormatContext *mFormatContext;
|
||||
int mVideoStreamId;
|
||||
AVCodecContext *mCodecContext;
|
||||
AVCodec *mCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
|
||||
int OpenFfmpeg();
|
||||
int ReopenFfmpeg();
|
||||
int CloseFfmpeg();
|
||||
static int FfmpegInterruptCallback(void *ctx);
|
||||
static void* ReopenFfmpegThreadCallback(void *ctx);
|
||||
bool mIsOpening;
|
||||
bool mCanCapture;
|
||||
int mOpenStart;
|
||||
pthread_t mReopenThread;
|
||||
int OpenFfmpeg();
|
||||
int ReopenFfmpeg();
|
||||
int CloseFfmpeg();
|
||||
static int FfmpegInterruptCallback(void *ctx);
|
||||
static void* ReopenFfmpegThreadCallback(void *ctx);
|
||||
bool mIsOpening;
|
||||
bool mCanCapture;
|
||||
int mOpenStart;
|
||||
pthread_t mReopenThread;
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
struct SwsContext *mConvertContext;
|
||||
struct SwsContext *mConvertContext;
|
||||
#endif
|
||||
|
||||
public:
|
||||
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~FfmpegCamera();
|
||||
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~FfmpegCamera();
|
||||
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Options() const { return( mOptions ); }
|
||||
const std::string &Method() const { return( mMethod ); }
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Options() const { return( mOptions ); }
|
||||
const std::string &Method() const { return( mMethod ); }
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
};
|
||||
|
||||
#endif // ZM_FFMPEG_CAMERA_H
|
||||
|
|
|
@ -36,28 +36,28 @@
|
|||
|
||||
FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture )
|
||||
{
|
||||
strncpy( path, p_path, sizeof(path) );
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
strncpy( path, p_path, sizeof(path) );
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
FileCamera::~FileCamera()
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void FileCamera::Initialise()
|
||||
{
|
||||
if ( !path[0] )
|
||||
{
|
||||
Error( "No path specified for file image" );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( !path[0] )
|
||||
{
|
||||
Error( "No path specified for file image" );
|
||||
exit( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
void FileCamera::Terminate()
|
||||
|
@ -66,26 +66,26 @@ void FileCamera::Terminate()
|
|||
|
||||
int FileCamera::PreCapture()
|
||||
{
|
||||
struct stat statbuf;
|
||||
if ( stat( path, &statbuf ) < 0 )
|
||||
{
|
||||
Error( "Can't stat %s: %s", path, strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
struct stat statbuf;
|
||||
if ( stat( path, &statbuf ) < 0 )
|
||||
{
|
||||
Error( "Can't stat %s: %s", path, strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
while ( (time( 0 ) - statbuf.st_mtime) < 1 )
|
||||
{
|
||||
usleep( 100000 );
|
||||
}
|
||||
return( 0 );
|
||||
while ( (time( 0 ) - statbuf.st_mtime) < 1 )
|
||||
{
|
||||
usleep( 100000 );
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int FileCamera::Capture( Image &image )
|
||||
{
|
||||
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
|
||||
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
|
||||
}
|
||||
|
||||
int FileCamera::PostCapture()
|
||||
{
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
|
|
@ -33,19 +33,19 @@
|
|||
class FileCamera : public Camera
|
||||
{
|
||||
protected:
|
||||
char path[PATH_MAX];
|
||||
char path[PATH_MAX];
|
||||
|
||||
public:
|
||||
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~FileCamera();
|
||||
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~FileCamera();
|
||||
|
||||
const char *Path() const { return( path ); }
|
||||
const char *Path() const { return( path ); }
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
};
|
||||
|
||||
#endif // ZM_FILE_CAMERA_H
|
||||
|
|
6660
src/zm_font.h
6660
src/zm_font.h
File diff suppressed because it is too large
Load Diff
9090
src/zm_image.cpp
9090
src/zm_image.cpp
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||
{
|
||||
m_Detectors = source.m_Detectors;
|
||||
m_Detectors = source.m_Detectors;
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
|||
*/
|
||||
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
||||
{
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
m_Detectors = source.m_Detectors;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImageAnalyser::~ImageAnalyser()
|
||||
{
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++It)
|
||||
delete *It;
|
||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||
It != m_Detectors.rend();
|
||||
++It)
|
||||
delete *It;
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser()
|
|||
*/
|
||||
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
||||
{
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
Event::StringSet zoneSet;
|
||||
int score = 0;
|
||||
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++It)
|
||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||
It != m_Detectors.end();
|
||||
++It)
|
||||
{
|
||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||
if (detect_score)
|
||||
{
|
||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||
if (detect_score)
|
||||
{
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
score += detect_score;
|
||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||
if (det_cause.length())
|
||||
det_cause += ", ";
|
||||
det_cause += (*It)->getDetectionCause();
|
||||
}
|
||||
return score;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,17 +23,17 @@ using namespace std;
|
|||
class ImageAnalyser {
|
||||
public:
|
||||
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
//!Default constructor.
|
||||
ImageAnalyser() {};
|
||||
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
//! Destructor.
|
||||
~ImageAnalyser();
|
||||
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
//! Copy constructor.
|
||||
ImageAnalyser(const ImageAnalyser& source);
|
||||
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
//! Overloaded operator=.
|
||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||
|
||||
private:
|
||||
|
||||
|
|
452
src/zm_jpeg.cpp
452
src/zm_jpeg.cpp
|
@ -32,65 +32,65 @@ static int jpeg_err_count = 0;
|
|||
|
||||
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||
}
|
||||
Error( "%s", buffer );
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||
{
|
||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||
}
|
||||
|
||||
longjmp( zmerr->setjmp_buffer, 1 );
|
||||
longjmp( zmerr->setjmp_buffer, 1 );
|
||||
}
|
||||
|
||||
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
||||
{
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
if ( msg_level < 0 )
|
||||
{
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning( "%s", buffer );
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level )
|
||||
{
|
||||
(zmerr->pub.format_message)( cinfo, buffer );
|
||||
Debug( msg_level, "%s", buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Expanded data destination object for memory */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
struct jpeg_destination_mgr pub; /* public fields */
|
||||
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
JOCTET *outbuffer; /* target buffer */
|
||||
int *outbuffer_size;
|
||||
JOCTET *buffer; /* start of buffer */
|
||||
} mem_destination_mgr;
|
||||
|
||||
typedef mem_destination_mgr * mem_dest_ptr;
|
||||
|
||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||
|
||||
/*
|
||||
* Initialize destination --- called by jpeg_start_compress
|
||||
|
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
|
|||
|
||||
static void init_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
|
||||
/* Allocate the output buffer --- it will be released when done with image */
|
||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
/* Allocate the output buffer --- it will be released when done with image */
|
||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
*(dest->outbuffer_size) = 0;
|
||||
*(dest->outbuffer_size) = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo)
|
|||
|
||||
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
dest->pub.next_output_byte = dest->buffer;
|
||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||
|
||||
return( TRUE );
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo)
|
|||
|
||||
static void term_destination (j_compress_ptr cinfo)
|
||||
{
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||
*(dest->outbuffer_size) += datacount;
|
||||
}
|
||||
if ( datacount > 0 )
|
||||
{
|
||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||
*(dest->outbuffer_size) += datacount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo)
|
|||
|
||||
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
||||
{
|
||||
mem_dest_ptr dest;
|
||||
mem_dest_ptr dest;
|
||||
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||
}
|
||||
/* The destination object is made permanent so that multiple JPEG images
|
||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||
* This makes it dangerous to use this manager and a different destination
|
||||
* manager serially with the same JPEG object, because their private object
|
||||
* sizes may be different. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->dest == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||
}
|
||||
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
dest = (mem_dest_ptr) cinfo->dest;
|
||||
dest->pub.init_destination = init_destination;
|
||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||
dest->pub.term_destination = term_destination;
|
||||
dest->outbuffer = outbuffer;
|
||||
dest->outbuffer_size = outbuffer_size;
|
||||
}
|
||||
|
||||
/* Expanded data source object for memory input */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
struct jpeg_source_mgr pub; /* public fields */
|
||||
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
JOCTET * inbuffer; /* source stream */
|
||||
int inbuffer_size;
|
||||
int inbuffer_size_hwm; /* High water mark */
|
||||
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
JOCTET * buffer; /* start of buffer */
|
||||
boolean start_of_data; /* have we gotten any data yet? */
|
||||
} mem_source_mgr;
|
||||
|
||||
typedef mem_source_mgr * mem_src_ptr;
|
||||
|
||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||
|
||||
/*
|
||||
* Initialize source --- called by jpeg_read_header
|
||||
|
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
|
|||
|
||||
static void init_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
/* We reset the empty-input-file flag for each image,
|
||||
* but we don't clear the input buffer.
|
||||
* This is correct behavior for reading a series of images from one source.
|
||||
*/
|
||||
src->start_of_data = TRUE;
|
||||
src->pub.bytes_in_buffer = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo)
|
|||
|
||||
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
size_t nbytes;
|
||||
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||
nbytes = src->inbuffer_size;
|
||||
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
if ( nbytes <= 0 )
|
||||
{
|
||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||
/* Insert a fake EOI marker */
|
||||
src->buffer[0] = (JOCTET) 0xFF;
|
||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||
nbytes = 2;
|
||||
}
|
||||
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
src->pub.next_input_byte = src->buffer;
|
||||
src->pub.bytes_in_buffer = nbytes;
|
||||
src->start_of_data = FALSE;
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
|||
|
||||
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||
{
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if ( num_bytes > 0 )
|
||||
/* Just a dumb implementation for now. Could use fseek() except
|
||||
* it doesn't work on pipes. Not clear that being smart is worth
|
||||
* any trouble anyway --- large skips are infrequent.
|
||||
*/
|
||||
if ( num_bytes > 0 )
|
||||
{
|
||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||
{
|
||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||
{
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* note we assume that fill_input_buffer will never return FALSE,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->pub.next_input_byte += (size_t) num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||
(void) fill_input_buffer(cinfo);
|
||||
/* note we assume that fill_input_buffer will never return FALSE,
|
||||
* so suspension need not be handled.
|
||||
*/
|
||||
}
|
||||
src->pub.next_input_byte += (size_t) num_bytes;
|
||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
|||
|
||||
static void term_source (j_decompress_ptr cinfo)
|
||||
{
|
||||
/* no work necessary here */
|
||||
/* no work necessary here */
|
||||
}
|
||||
|
||||
|
||||
|
@ -354,113 +354,113 @@ static void term_source (j_decompress_ptr cinfo)
|
|||
|
||||
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||
{
|
||||
mem_src_ptr src;
|
||||
|
||||
/* The source object and input buffer are made permanent so that a series
|
||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||
* only before the first one. (If we discarded the buffer at the end of
|
||||
* one image, we'd likely lose the start of the next one.)
|
||||
* This makes it unsafe to use this manager and a different source
|
||||
* manager serially with the same JPEG object. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->src == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||
{
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
}
|
||||
mem_src_ptr src;
|
||||
|
||||
/* The source object and input buffer are made permanent so that a series
|
||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||
* only before the first one. (If we discarded the buffer at the end of
|
||||
* one image, we'd likely lose the start of the next one.)
|
||||
* This makes it unsafe to use this manager and a different source
|
||||
* manager serially with the same JPEG object. Caveat programmer.
|
||||
*/
|
||||
if ( cinfo->src == NULL )
|
||||
{
|
||||
/* first time for this JPEG object? */
|
||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->inbuffer = (JOCTET *)inbuffer;
|
||||
src->inbuffer_size = inbuffer_size;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||
{
|
||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||
src->inbuffer_size_hwm = inbuffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
src = (mem_src_ptr) cinfo->src;
|
||||
src->pub.init_source = init_source;
|
||||
src->pub.fill_input_buffer = fill_input_buffer;
|
||||
src->pub.skip_input_data = skip_input_data;
|
||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||
src->pub.term_source = term_source;
|
||||
src->inbuffer = (JOCTET *)inbuffer;
|
||||
src->inbuffer_size = inbuffer_size;
|
||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||
}
|
||||
|
||||
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
static const JHUFF_TBL dclumin = {
|
||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL dcchrome = {
|
||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||
static const JHUFF_TBL dclumin = {
|
||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL dcchrome = {
|
||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL aclumin = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
static const JHUFF_TBL acchrome = {
|
||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||
0xf9, 0xfa },
|
||||
FALSE
|
||||
};
|
||||
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ extern "C"
|
|||
/* Stuff for overriden error handlers */
|
||||
struct zm_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
};
|
||||
|
||||
typedef struct zm_error_mgr *zm_error_ptr;
|
||||
|
|
|
@ -25,102 +25,102 @@
|
|||
// Do all the buffer checking work here to avoid unnecessary locking
|
||||
void* LibvlcLockBuffer(void* opaque, void** planes)
|
||||
{
|
||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||
data->mutex.lock();
|
||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||
data->mutex.lock();
|
||||
|
||||
uint8_t* buffer = data->buffer;
|
||||
data->buffer = data->prevBuffer;
|
||||
data->prevBuffer = buffer;
|
||||
uint8_t* buffer = data->buffer;
|
||||
data->buffer = data->prevBuffer;
|
||||
data->prevBuffer = buffer;
|
||||
|
||||
*planes = data->buffer;
|
||||
return NULL;
|
||||
*planes = data->buffer;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes)
|
||||
{
|
||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||
|
||||
bool newFrame = false;
|
||||
for(uint32_t i = 0; i < data->bufferSize; i++)
|
||||
bool newFrame = false;
|
||||
for(uint32_t i = 0; i < data->bufferSize; i++)
|
||||
{
|
||||
if(data->buffer[i] != data->prevBuffer[i])
|
||||
{
|
||||
if(data->buffer[i] != data->prevBuffer[i])
|
||||
{
|
||||
newFrame = true;
|
||||
break;
|
||||
}
|
||||
newFrame = true;
|
||||
break;
|
||||
}
|
||||
data->mutex.unlock();
|
||||
}
|
||||
data->mutex.unlock();
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
|
||||
if(newFrame || difftime(now, data->prevTime) >= 0.8)
|
||||
{
|
||||
data->prevTime = now;
|
||||
data->newImage.updateValueSignal(true);
|
||||
}
|
||||
time_t now;
|
||||
time(&now);
|
||||
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
|
||||
if(newFrame || difftime(now, data->prevTime) >= 0.8)
|
||||
{
|
||||
data->prevTime = now;
|
||||
data->newImage.updateValueSignal(true);
|
||||
}
|
||||
}
|
||||
|
||||
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ),
|
||||
mMethod( p_method ),
|
||||
mOptions( p_options )
|
||||
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
mPath( p_path ),
|
||||
mMethod( p_method ),
|
||||
mOptions( p_options )
|
||||
{
|
||||
mLibvlcInstance = NULL;
|
||||
mLibvlcMedia = NULL;
|
||||
mLibvlcMediaPlayer = NULL;
|
||||
mLibvlcData.buffer = NULL;
|
||||
mLibvlcData.prevBuffer = NULL;
|
||||
mLibvlcInstance = NULL;
|
||||
mLibvlcMedia = NULL;
|
||||
mLibvlcMediaPlayer = NULL;
|
||||
mLibvlcData.buffer = NULL;
|
||||
mLibvlcData.prevBuffer = NULL;
|
||||
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
mTargetChroma = "RV32";
|
||||
mBpp = 4;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
mTargetChroma = "RV24";
|
||||
mBpp = 3;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
mTargetChroma = "GREY";
|
||||
mBpp = 1;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
mTargetChroma = "RV32";
|
||||
mBpp = 4;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
mTargetChroma = "RV24";
|
||||
mBpp = 3;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
mTargetChroma = "GREY";
|
||||
mBpp = 1;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
LibvlcCamera::~LibvlcCamera()
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
if(mLibvlcMediaPlayer != NULL)
|
||||
{
|
||||
libvlc_media_player_release(mLibvlcMediaPlayer);
|
||||
mLibvlcMediaPlayer = NULL;
|
||||
}
|
||||
if(mLibvlcMedia != NULL)
|
||||
{
|
||||
libvlc_media_release(mLibvlcMedia);
|
||||
mLibvlcMedia = NULL;
|
||||
}
|
||||
if(mLibvlcInstance != NULL)
|
||||
{
|
||||
libvlc_release(mLibvlcInstance);
|
||||
mLibvlcInstance = NULL;
|
||||
}
|
||||
if (mOptArgV != NULL)
|
||||
{
|
||||
delete[] mOptArgV;
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
if(mLibvlcMediaPlayer != NULL)
|
||||
{
|
||||
libvlc_media_player_release(mLibvlcMediaPlayer);
|
||||
mLibvlcMediaPlayer = NULL;
|
||||
}
|
||||
if(mLibvlcMedia != NULL)
|
||||
{
|
||||
libvlc_media_release(mLibvlcMedia);
|
||||
mLibvlcMedia = NULL;
|
||||
}
|
||||
if(mLibvlcInstance != NULL)
|
||||
{
|
||||
libvlc_release(mLibvlcInstance);
|
||||
mLibvlcInstance = NULL;
|
||||
}
|
||||
if (mOptArgV != NULL)
|
||||
{
|
||||
delete[] mOptArgV;
|
||||
}
|
||||
}
|
||||
|
||||
void LibvlcCamera::Initialise()
|
||||
|
@ -129,91 +129,91 @@ void LibvlcCamera::Initialise()
|
|||
|
||||
void LibvlcCamera::Terminate()
|
||||
{
|
||||
libvlc_media_player_stop(mLibvlcMediaPlayer);
|
||||
if(mLibvlcData.buffer != NULL)
|
||||
{
|
||||
zm_freealigned(mLibvlcData.buffer);
|
||||
}
|
||||
if(mLibvlcData.prevBuffer != NULL)
|
||||
{
|
||||
zm_freealigned(mLibvlcData.prevBuffer);
|
||||
}
|
||||
libvlc_media_player_stop(mLibvlcMediaPlayer);
|
||||
if(mLibvlcData.buffer != NULL)
|
||||
{
|
||||
zm_freealigned(mLibvlcData.buffer);
|
||||
}
|
||||
if(mLibvlcData.prevBuffer != NULL)
|
||||
{
|
||||
zm_freealigned(mLibvlcData.prevBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
int LibvlcCamera::PrimeCapture()
|
||||
{
|
||||
Info("Priming capture from %s", mPath.c_str());
|
||||
Info("Priming capture from %s", mPath.c_str());
|
||||
|
||||
StringVector opVect = split(Options(), ",");
|
||||
StringVector opVect = split(Options(), ",");
|
||||
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" )
|
||||
opVect.push_back("--rtsp-mcast");
|
||||
else if ( Method() == "rtpRtsp" )
|
||||
opVect.push_back("--rtsp-tcp");
|
||||
else if ( Method() == "rtpRtspHttp" )
|
||||
opVect.push_back("--rtsp-http");
|
||||
// Set transport method as specified by method field, rtpUni is default
|
||||
if ( Method() == "rtpMulti" )
|
||||
opVect.push_back("--rtsp-mcast");
|
||||
else if ( Method() == "rtpRtsp" )
|
||||
opVect.push_back("--rtsp-tcp");
|
||||
else if ( Method() == "rtpRtspHttp" )
|
||||
opVect.push_back("--rtsp-http");
|
||||
|
||||
if (opVect.size() > 0)
|
||||
{
|
||||
mOptArgV = new char*[opVect.size()];
|
||||
Debug(2, "Number of Options: %d",opVect.size());
|
||||
for (size_t i=0; i< opVect.size(); i++) {
|
||||
opVect[i] = trimSpaces(opVect[i]);
|
||||
mOptArgV[i] = (char *)opVect[i].c_str();
|
||||
Debug(2, "set option %d to '%s'", i, opVect[i].c_str());
|
||||
}
|
||||
if (opVect.size() > 0)
|
||||
{
|
||||
mOptArgV = new char*[opVect.size()];
|
||||
Debug(2, "Number of Options: %d",opVect.size());
|
||||
for (size_t i=0; i< opVect.size(); i++) {
|
||||
opVect[i] = trimSpaces(opVect[i]);
|
||||
mOptArgV[i] = (char *)opVect[i].c_str();
|
||||
Debug(2, "set option %d to '%s'", i, opVect[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV);
|
||||
if(mLibvlcInstance == NULL)
|
||||
Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg());
|
||||
mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV);
|
||||
if(mLibvlcInstance == NULL)
|
||||
Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg());
|
||||
|
||||
mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str());
|
||||
if(mLibvlcMedia == NULL)
|
||||
Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||
mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str());
|
||||
if(mLibvlcMedia == NULL)
|
||||
Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||
|
||||
mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia);
|
||||
if(mLibvlcMediaPlayer == NULL)
|
||||
Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||
mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia);
|
||||
if(mLibvlcMediaPlayer == NULL)
|
||||
Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||
|
||||
libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
|
||||
libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData);
|
||||
libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
|
||||
libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData);
|
||||
|
||||
mLibvlcData.bufferSize = width * height * mBpp;
|
||||
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
|
||||
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||
mLibvlcData.bufferSize = width * height * mBpp;
|
||||
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
|
||||
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||
|
||||
mLibvlcData.newImage.setValueImmediate(false);
|
||||
mLibvlcData.newImage.setValueImmediate(false);
|
||||
|
||||
libvlc_media_player_play(mLibvlcMediaPlayer);
|
||||
libvlc_media_player_play(mLibvlcMediaPlayer);
|
||||
|
||||
return(0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int LibvlcCamera::PreCapture()
|
||||
{
|
||||
return(0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
// Should not return -1 as cancels capture. Always wait for image if available.
|
||||
int LibvlcCamera::Capture( Image &image )
|
||||
{
|
||||
while(!mLibvlcData.newImage.getValueImmediate())
|
||||
mLibvlcData.newImage.getUpdatedValue(1);
|
||||
while(!mLibvlcData.newImage.getValueImmediate())
|
||||
mLibvlcData.newImage.getUpdatedValue(1);
|
||||
|
||||
mLibvlcData.mutex.lock();
|
||||
image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp);
|
||||
mLibvlcData.newImage.setValueImmediate(false);
|
||||
mLibvlcData.mutex.unlock();
|
||||
mLibvlcData.mutex.lock();
|
||||
image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp);
|
||||
mLibvlcData.newImage.setValueImmediate(false);
|
||||
mLibvlcData.mutex.unlock();
|
||||
|
||||
return (0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int LibvlcCamera::PostCapture()
|
||||
{
|
||||
return(0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBVLC
|
||||
|
|
|
@ -33,44 +33,44 @@
|
|||
// Used by libvlc callbacks
|
||||
struct LibvlcPrivateData
|
||||
{
|
||||
uint8_t* buffer;
|
||||
uint8_t* prevBuffer;
|
||||
time_t prevTime;
|
||||
uint32_t bufferSize;
|
||||
Mutex mutex;
|
||||
ThreadData<bool> newImage;
|
||||
uint8_t* buffer;
|
||||
uint8_t* prevBuffer;
|
||||
time_t prevTime;
|
||||
uint32_t bufferSize;
|
||||
Mutex mutex;
|
||||
ThreadData<bool> newImage;
|
||||
};
|
||||
|
||||
class LibvlcCamera : public Camera
|
||||
{
|
||||
protected:
|
||||
std::string mPath;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
char **mOptArgV;
|
||||
LibvlcPrivateData mLibvlcData;
|
||||
std::string mTargetChroma;
|
||||
uint8_t mBpp;
|
||||
std::string mPath;
|
||||
std::string mMethod;
|
||||
std::string mOptions;
|
||||
char **mOptArgV;
|
||||
LibvlcPrivateData mLibvlcData;
|
||||
std::string mTargetChroma;
|
||||
uint8_t mBpp;
|
||||
|
||||
libvlc_instance_t *mLibvlcInstance;
|
||||
libvlc_media_t *mLibvlcMedia;
|
||||
libvlc_media_player_t *mLibvlcMediaPlayer;
|
||||
libvlc_instance_t *mLibvlcInstance;
|
||||
libvlc_media_t *mLibvlcMedia;
|
||||
libvlc_media_player_t *mLibvlcMediaPlayer;
|
||||
|
||||
public:
|
||||
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~LibvlcCamera();
|
||||
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~LibvlcCamera();
|
||||
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Options() const { return( mOptions ); }
|
||||
const std::string &Method() const { return( mMethod ); }
|
||||
const std::string &Path() const { return( mPath ); }
|
||||
const std::string &Options() const { return( mOptions ); }
|
||||
const std::string &Method() const { return( mMethod ); }
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBVLC
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -47,99 +47,99 @@ class LocalCamera : public Camera
|
|||
{
|
||||
protected:
|
||||
#if ZM_HAS_V4L2
|
||||
struct V4L2MappedBuffer
|
||||
{
|
||||
void *start;
|
||||
size_t length;
|
||||
};
|
||||
struct V4L2MappedBuffer
|
||||
{
|
||||
void *start;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct V4L2Data
|
||||
{
|
||||
v4l2_cropcap cropcap;
|
||||
v4l2_crop crop;
|
||||
v4l2_format fmt;
|
||||
v4l2_requestbuffers reqbufs;
|
||||
V4L2MappedBuffer *buffers;
|
||||
v4l2_buffer *bufptr;
|
||||
};
|
||||
struct V4L2Data
|
||||
{
|
||||
v4l2_cropcap cropcap;
|
||||
v4l2_crop crop;
|
||||
v4l2_format fmt;
|
||||
v4l2_requestbuffers reqbufs;
|
||||
V4L2MappedBuffer *buffers;
|
||||
v4l2_buffer *bufptr;
|
||||
};
|
||||
#endif // ZM_HAS_V4L2
|
||||
|
||||
#if ZM_HAS_V4L1
|
||||
struct V4L1Data
|
||||
{
|
||||
int active_frame;
|
||||
video_mbuf frames;
|
||||
video_mmap *buffers;
|
||||
unsigned char *bufptr;
|
||||
};
|
||||
struct V4L1Data
|
||||
{
|
||||
int active_frame;
|
||||
video_mbuf frames;
|
||||
video_mmap *buffers;
|
||||
unsigned char *bufptr;
|
||||
};
|
||||
#endif // ZM_HAS_V4L1
|
||||
|
||||
protected:
|
||||
std::string device;
|
||||
int channel;
|
||||
int standard;
|
||||
int palette;
|
||||
bool device_prime;
|
||||
bool channel_prime;
|
||||
int channel_index;
|
||||
unsigned int extras;
|
||||
std::string device;
|
||||
int channel;
|
||||
int standard;
|
||||
int palette;
|
||||
bool device_prime;
|
||||
bool channel_prime;
|
||||
int channel_index;
|
||||
unsigned int extras;
|
||||
|
||||
unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */
|
||||
convert_fptr_t conversion_fptr; /* Pointer to conversion function used */
|
||||
unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */
|
||||
convert_fptr_t conversion_fptr; /* Pointer to conversion function used */
|
||||
|
||||
uint32_t AutoSelectFormat(int p_colours);
|
||||
uint32_t AutoSelectFormat(int p_colours);
|
||||
|
||||
static int camera_count;
|
||||
static int channel_count;
|
||||
static int channels[VIDEO_MAX_FRAME];
|
||||
static int standards[VIDEO_MAX_FRAME];
|
||||
static int vid_fd;
|
||||
static int v4l_version;
|
||||
bool v4l_multi_buffer;
|
||||
unsigned int v4l_captures_per_frame;
|
||||
static int camera_count;
|
||||
static int channel_count;
|
||||
static int channels[VIDEO_MAX_FRAME];
|
||||
static int standards[VIDEO_MAX_FRAME];
|
||||
static int vid_fd;
|
||||
static int v4l_version;
|
||||
bool v4l_multi_buffer;
|
||||
unsigned int v4l_captures_per_frame;
|
||||
|
||||
#if ZM_HAS_V4L2
|
||||
static V4L2Data v4l2_data;
|
||||
static V4L2Data v4l2_data;
|
||||
#endif // ZM_HAS_V4L2
|
||||
#if ZM_HAS_V4L1
|
||||
static V4L1Data v4l1_data;
|
||||
static V4L1Data v4l1_data;
|
||||
#endif // ZM_HAS_V4L1
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
static AVFrame **capturePictures;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
_AVPIXELFORMAT capturePixFormat;
|
||||
struct SwsContext *imgConversionContext;
|
||||
AVFrame *tmpPicture;
|
||||
static AVFrame **capturePictures;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
_AVPIXELFORMAT capturePixFormat;
|
||||
struct SwsContext *imgConversionContext;
|
||||
AVFrame *tmpPicture;
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
static LocalCamera *last_camera;
|
||||
static LocalCamera *last_camera;
|
||||
|
||||
public:
|
||||
LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0);
|
||||
~LocalCamera();
|
||||
LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0);
|
||||
~LocalCamera();
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
||||
const std::string &Device() const { return( device ); }
|
||||
const std::string &Device() const { return( device ); }
|
||||
|
||||
int Channel() const { return( channel ); }
|
||||
int Standard() const { return( standard ); }
|
||||
int Palette() const { return( palette ); }
|
||||
int Extras() const { return( extras ); }
|
||||
int Channel() const { return( channel ); }
|
||||
int Standard() const { return( standard ); }
|
||||
int Palette() const { return( palette ); }
|
||||
int Extras() const { return( extras ); }
|
||||
|
||||
int Brightness( int p_brightness=-1 );
|
||||
int Hue( int p_hue=-1 );
|
||||
int Colour( int p_colour=-1 );
|
||||
int Contrast( int p_contrast=-1 );
|
||||
int Brightness( int p_brightness=-1 );
|
||||
int Hue( int p_hue=-1 );
|
||||
int Colour( int p_colour=-1 );
|
||||
int Contrast( int p_contrast=-1 );
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
|
||||
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
||||
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
||||
};
|
||||
|
||||
#endif // ZM_HAS_V4L
|
||||
|
|
File diff suppressed because it is too large
Load Diff
298
src/zm_logger.h
298
src/zm_logger.h
|
@ -33,190 +33,190 @@
|
|||
class Logger
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
enum {
|
||||
NOOPT=-6,
|
||||
NOLOG,
|
||||
PANIC,
|
||||
FATAL,
|
||||
ERROR,
|
||||
WARNING,
|
||||
INFO,
|
||||
DEBUG1,
|
||||
DEBUG2,
|
||||
DEBUG3,
|
||||
DEBUG4,
|
||||
DEBUG5,
|
||||
DEBUG6,
|
||||
DEBUG7,
|
||||
DEBUG8,
|
||||
DEBUG9
|
||||
};
|
||||
|
||||
typedef int Level;
|
||||
typedef int Level;
|
||||
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
typedef std::map<Level,std::string> StringMap;
|
||||
typedef std::map<Level,int> IntMap;
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
int mSyslogLevel;
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
int mTermLevel;
|
||||
int mDatabaseLevel;
|
||||
int mFileLevel;
|
||||
int mSyslogLevel;
|
||||
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
|
||||
public:
|
||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||
mTermLevel( termLevel ),
|
||||
mDatabaseLevel( databaseLevel ),
|
||||
mFileLevel( fileLevel ),
|
||||
mSyslogLevel( syslogLevel ),
|
||||
mLogPath( logPath ),
|
||||
mLogFile( logFile )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static bool smInitialised;
|
||||
static Logger *smInstance;
|
||||
|
||||
static StringMap smCodes;
|
||||
static IntMap smSyslogPriorities;
|
||||
|
||||
private:
|
||||
bool mInitialised;
|
||||
|
||||
std::string mId;
|
||||
std::string mIdRoot;
|
||||
std::string mIdArgs;
|
||||
|
||||
Level mLevel; // Level that is currently in operation
|
||||
Level mTermLevel; // Maximum level output via terminal
|
||||
Level mDatabaseLevel; // Maximum level output via database
|
||||
Level mFileLevel; // Maximum level output via file
|
||||
Level mSyslogLevel; // Maximum level output via syslog
|
||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||
|
||||
bool mDbConnected;
|
||||
MYSQL mDbConnection;
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
public:
|
||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||
mTermLevel( termLevel ),
|
||||
mDatabaseLevel( databaseLevel ),
|
||||
mFileLevel( fileLevel ),
|
||||
mSyslogLevel( syslogLevel ),
|
||||
mLogPath( logPath ),
|
||||
mLogFile( logFile )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static void usrHandler( int sig );
|
||||
static bool smInitialised;
|
||||
static Logger *smInstance;
|
||||
|
||||
public:
|
||||
friend void logInit( const char *name, const Options &options );
|
||||
friend void logTerm();
|
||||
|
||||
static Logger *fetch()
|
||||
{
|
||||
if ( !smInstance )
|
||||
{
|
||||
smInstance = new Logger();
|
||||
Options options;
|
||||
smInstance->initialise( "undef", options );
|
||||
}
|
||||
return( smInstance );
|
||||
}
|
||||
static StringMap smCodes;
|
||||
static IntMap smSyslogPriorities;
|
||||
|
||||
private:
|
||||
Logger();
|
||||
~Logger();
|
||||
bool mInitialised;
|
||||
|
||||
public:
|
||||
void initialise( const std::string &id, const Options &options );
|
||||
void terminate();
|
||||
std::string mId;
|
||||
std::string mIdRoot;
|
||||
std::string mIdArgs;
|
||||
|
||||
Level mLevel; // Level that is currently in operation
|
||||
Level mTermLevel; // Maximum level output via terminal
|
||||
Level mDatabaseLevel; // Maximum level output via database
|
||||
Level mFileLevel; // Maximum level output via file
|
||||
Level mSyslogLevel; // Maximum level output via syslog
|
||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||
|
||||
bool mDbConnected;
|
||||
MYSQL mDbConnection;
|
||||
std::string mLogPath;
|
||||
std::string mLogFile;
|
||||
FILE *mLogFileFP;
|
||||
|
||||
bool mHasTerm;
|
||||
bool mFlush;
|
||||
|
||||
private:
|
||||
int limit( int level )
|
||||
{
|
||||
if ( level > DEBUG9 )
|
||||
return( DEBUG9 );
|
||||
if ( level < NOLOG )
|
||||
return( NOLOG );
|
||||
return( level );
|
||||
}
|
||||
|
||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||
char *getTargettedEnv( const std::string &name );
|
||||
|
||||
void loadEnv();
|
||||
static void usrHandler( int sig );
|
||||
|
||||
public:
|
||||
const std::string &id() const
|
||||
friend void logInit( const char *name, const Options &options );
|
||||
friend void logTerm();
|
||||
|
||||
static Logger *fetch()
|
||||
{
|
||||
if ( !smInstance )
|
||||
{
|
||||
return( mId );
|
||||
smInstance = new Logger();
|
||||
Options options;
|
||||
smInstance->initialise( "undef", options );
|
||||
}
|
||||
|
||||
const std::string &id( const std::string &id );
|
||||
|
||||
Level level() const
|
||||
{
|
||||
return( mLevel );
|
||||
}
|
||||
Level level( Level=NOOPT );
|
||||
|
||||
bool debugOn()
|
||||
{
|
||||
return( mEffectiveLevel >= DEBUG1 );
|
||||
}
|
||||
|
||||
Level termLevel( Level=NOOPT );
|
||||
Level databaseLevel( Level=NOOPT );
|
||||
Level fileLevel( Level=NOOPT );
|
||||
Level syslogLevel( Level=NOOPT );
|
||||
return( smInstance );
|
||||
}
|
||||
|
||||
private:
|
||||
void logFile( const std::string &logFile );
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openSyslog();
|
||||
void closeSyslog();
|
||||
void closeDatabase();
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
public:
|
||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||
void initialise( const std::string &id, const Options &options );
|
||||
void terminate();
|
||||
|
||||
private:
|
||||
int limit( int level )
|
||||
{
|
||||
if ( level > DEBUG9 )
|
||||
return( DEBUG9 );
|
||||
if ( level < NOLOG )
|
||||
return( NOLOG );
|
||||
return( level );
|
||||
}
|
||||
|
||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||
char *getTargettedEnv( const std::string &name );
|
||||
|
||||
void loadEnv();
|
||||
|
||||
public:
|
||||
const std::string &id() const
|
||||
{
|
||||
return( mId );
|
||||
}
|
||||
|
||||
const std::string &id( const std::string &id );
|
||||
|
||||
Level level() const
|
||||
{
|
||||
return( mLevel );
|
||||
}
|
||||
Level level( Level=NOOPT );
|
||||
|
||||
bool debugOn()
|
||||
{
|
||||
return( mEffectiveLevel >= DEBUG1 );
|
||||
}
|
||||
|
||||
Level termLevel( Level=NOOPT );
|
||||
Level databaseLevel( Level=NOOPT );
|
||||
Level fileLevel( Level=NOOPT );
|
||||
Level syslogLevel( Level=NOOPT );
|
||||
|
||||
private:
|
||||
void logFile( const std::string &logFile );
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void openSyslog();
|
||||
void closeSyslog();
|
||||
void closeDatabase();
|
||||
|
||||
public:
|
||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||
};
|
||||
|
||||
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
||||
void logTerm();
|
||||
inline const std::string &logId()
|
||||
{
|
||||
return( Logger::fetch()->id() );
|
||||
return( Logger::fetch()->id() );
|
||||
}
|
||||
inline Logger::Level logLevel()
|
||||
{
|
||||
return( Logger::fetch()->level() );
|
||||
return( Logger::fetch()->level() );
|
||||
}
|
||||
inline void logCapLevel( Logger::Level level )
|
||||
{
|
||||
Logger::fetch()->level( level );
|
||||
Logger::fetch()->level( level );
|
||||
}
|
||||
inline Logger::Level logDebugging()
|
||||
{
|
||||
return( Logger::fetch()->debugOn() );
|
||||
return( Logger::fetch()->debugOn() );
|
||||
}
|
||||
|
||||
#define logPrintf(logLevel,params...) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||
}
|
||||
|
||||
#define logHexdump(logLevel,data,len) {\
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
if ( logLevel <= Logger::fetch()->level() )\
|
||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||
}
|
||||
|
||||
/* Debug compiled out */
|
||||
#ifndef DBG_OFF
|
||||
|
@ -228,16 +228,16 @@ inline Logger::Level logDebugging()
|
|||
#endif
|
||||
|
||||
/* Standard debug calls */
|
||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||
#define Log() Info("Log")
|
||||
#ifdef __GNUC__
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||
#else
|
||||
#define Enter(level)
|
||||
#define Exit(level)
|
||||
|
|
|
@ -24,138 +24,138 @@
|
|||
#include "zm.h"
|
||||
|
||||
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
||||
uint8_t* retptr;
|
||||
uint8_t* retptr;
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||
return NULL;
|
||||
|
||||
return retptr;
|
||||
return retptr;
|
||||
#else
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
uint8_t* alloc;
|
||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
if(retptr == NULL)
|
||||
return NULL;
|
||||
|
||||
alloc = retptr + sizeof(void*);
|
||||
alloc = retptr + sizeof(void*);
|
||||
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
if(((long)alloc % reqalignment) != 0)
|
||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||
|
||||
return alloc;
|
||||
return alloc;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void zm_freealigned(void* ptr) {
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
#else
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
return( (char *)s );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
return( (char *)s );
|
||||
}
|
||||
}
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline char *memstr( register const char *s, const char *n, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !n )
|
||||
return( 0 );
|
||||
|
||||
if ( !*n )
|
||||
return( (char *)s );
|
||||
|
||||
register unsigned int i,j,k;
|
||||
size_t n_len = strlen( n );
|
||||
|
||||
for ( i = 0; i < limit; i++, s++ )
|
||||
{
|
||||
if ( *s != *n )
|
||||
continue;
|
||||
j = 1;
|
||||
k = 1;
|
||||
while ( true )
|
||||
{
|
||||
if ( k >= n_len )
|
||||
return( (char *)s );
|
||||
if ( s[j++] != n[k++] )
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( limit <= 0 || !s || !n )
|
||||
return( 0 );
|
||||
|
||||
if ( !*n )
|
||||
return( (char *)s );
|
||||
|
||||
register unsigned int i,j,k;
|
||||
size_t n_len = strlen( n );
|
||||
|
||||
for ( i = 0; i < limit; i++, s++ )
|
||||
{
|
||||
if ( *s != *n )
|
||||
continue;
|
||||
j = 1;
|
||||
k = 1;
|
||||
while ( true )
|
||||
{
|
||||
if ( k >= n_len )
|
||||
return( (char *)s );
|
||||
if ( s[j++] != n[k++] )
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !accept || !*accept )
|
||||
return( 0 );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
register unsigned int i,j;
|
||||
size_t acc_len = strlen( accept );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
register bool found = false;
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
register bool found = false;
|
||||
for ( j = 0; j < acc_len; j++ )
|
||||
{
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
if ( *s == accept[j] )
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
if ( !found )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
}
|
||||
|
||||
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
||||
{
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
if ( limit <= 0 || !s || !reject )
|
||||
return( 0 );
|
||||
|
||||
if ( !*reject )
|
||||
return( limit );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t rej_len = strlen( reject );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < rej_len; j++ )
|
||||
{
|
||||
if ( *s == reject[j] )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !*reject )
|
||||
return( limit );
|
||||
|
||||
register unsigned int i,j;
|
||||
size_t rej_len = strlen( reject );
|
||||
|
||||
for ( i = 0; i < limit; s++, i++ )
|
||||
{
|
||||
for ( j = 0; j < rej_len; j++ )
|
||||
{
|
||||
if ( *s == reject[j] )
|
||||
{
|
||||
return( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
return( limit );
|
||||
}
|
||||
|
||||
#endif // ZM_MEM_UTILS_H
|
||||
|
|
7662
src/zm_monitor.cpp
7662
src/zm_monitor.cpp
File diff suppressed because it is too large
Load Diff
714
src/zm_monitor.h
714
src/zm_monitor.h
|
@ -50,396 +50,396 @@ class Monitor
|
|||
friend class MonitorStream;
|
||||
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
QUERY=0,
|
||||
CAPTURE,
|
||||
ANALYSIS
|
||||
} Purpose;
|
||||
typedef enum
|
||||
{
|
||||
QUERY=0,
|
||||
CAPTURE,
|
||||
ANALYSIS
|
||||
} Purpose;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NONE=1,
|
||||
MONITOR,
|
||||
MODECT,
|
||||
RECORD,
|
||||
MOCORD,
|
||||
NODECT
|
||||
} Function;
|
||||
typedef enum
|
||||
{
|
||||
NONE=1,
|
||||
MONITOR,
|
||||
MODECT,
|
||||
RECORD,
|
||||
MOCORD,
|
||||
NODECT
|
||||
} Function;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ROTATE_0=1,
|
||||
ROTATE_90,
|
||||
ROTATE_180,
|
||||
ROTATE_270,
|
||||
FLIP_HORI,
|
||||
FLIP_VERT
|
||||
} Orientation;
|
||||
typedef enum
|
||||
{
|
||||
ROTATE_0=1,
|
||||
ROTATE_90,
|
||||
ROTATE_180,
|
||||
ROTATE_270,
|
||||
FLIP_HORI,
|
||||
FLIP_VERT
|
||||
} Orientation;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IDLE,
|
||||
PREALARM,
|
||||
ALARM,
|
||||
ALERT,
|
||||
TAPE
|
||||
} State;
|
||||
typedef enum
|
||||
{
|
||||
IDLE,
|
||||
PREALARM,
|
||||
ALARM,
|
||||
ALERT,
|
||||
TAPE
|
||||
} State;
|
||||
|
||||
protected:
|
||||
typedef std::set<Zone *> ZoneSet;
|
||||
typedef std::set<Zone *> ZoneSet;
|
||||
|
||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||
|
||||
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
||||
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
||||
|
||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size; /* +0 */
|
||||
uint32_t last_write_index; /* +4 */
|
||||
uint32_t last_read_index; /* +8 */
|
||||
uint32_t state; /* +12 */
|
||||
uint32_t last_event; /* +16 */
|
||||
uint32_t action; /* +20 */
|
||||
int32_t brightness; /* +24 */
|
||||
int32_t hue; /* +28 */
|
||||
int32_t colour; /* +32 */
|
||||
int32_t contrast; /* +36 */
|
||||
int32_t alarm_x; /* +40 */
|
||||
int32_t alarm_y; /* +44 */
|
||||
uint8_t valid; /* +48 */
|
||||
uint8_t active; /* +49 */
|
||||
uint8_t signal; /* +50 */
|
||||
uint8_t format; /* +51 */
|
||||
uint32_t imagesize; /* +52 */
|
||||
uint32_t epadding1; /* +56 */
|
||||
uint32_t epadding2; /* +60 */
|
||||
/*
|
||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||
*/
|
||||
union { /* +64 */
|
||||
time_t last_write_time;
|
||||
uint64_t extrapad1;
|
||||
};
|
||||
union { /* +72 */
|
||||
time_t last_read_time;
|
||||
uint64_t extrapad2;
|
||||
};
|
||||
uint8_t control_state[256]; /* +80 */
|
||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size; /* +0 */
|
||||
uint32_t last_write_index; /* +4 */
|
||||
uint32_t last_read_index; /* +8 */
|
||||
uint32_t state; /* +12 */
|
||||
uint32_t last_event; /* +16 */
|
||||
uint32_t action; /* +20 */
|
||||
int32_t brightness; /* +24 */
|
||||
int32_t hue; /* +28 */
|
||||
int32_t colour; /* +32 */
|
||||
int32_t contrast; /* +36 */
|
||||
int32_t alarm_x; /* +40 */
|
||||
int32_t alarm_y; /* +44 */
|
||||
uint8_t valid; /* +48 */
|
||||
uint8_t active; /* +49 */
|
||||
uint8_t signal; /* +50 */
|
||||
uint8_t format; /* +51 */
|
||||
uint32_t imagesize; /* +52 */
|
||||
uint32_t epadding1; /* +56 */
|
||||
uint32_t epadding2; /* +60 */
|
||||
/*
|
||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||
*/
|
||||
union { /* +64 */
|
||||
time_t last_write_time;
|
||||
uint64_t extrapad1;
|
||||
};
|
||||
union { /* +72 */
|
||||
time_t last_read_time;
|
||||
uint64_t extrapad2;
|
||||
};
|
||||
uint8_t control_state[256]; /* +80 */
|
||||
|
||||
} SharedData;
|
||||
} SharedData;
|
||||
|
||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||
|
||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t trigger_state;
|
||||
uint32_t trigger_score;
|
||||
uint32_t padding;
|
||||
char trigger_cause[32];
|
||||
char trigger_text[256];
|
||||
char trigger_showtext[256];
|
||||
} TriggerData;
|
||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t trigger_state;
|
||||
uint32_t trigger_score;
|
||||
uint32_t padding;
|
||||
char trigger_cause[32];
|
||||
char trigger_text[256];
|
||||
char trigger_showtext[256];
|
||||
} TriggerData;
|
||||
|
||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||
struct Snapshot
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
Image *image;
|
||||
void* padding;
|
||||
};
|
||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||
struct Snapshot
|
||||
{
|
||||
struct timeval *timestamp;
|
||||
Image *image;
|
||||
void* padding;
|
||||
};
|
||||
|
||||
class MonitorLink
|
||||
{
|
||||
protected:
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
class MonitorLink
|
||||
{
|
||||
protected:
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
|
||||
bool connected;
|
||||
time_t last_connect_time;
|
||||
bool connected;
|
||||
time_t last_connect_time;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
#else // ZM_MEM_MAPPED
|
||||
int shm_id;
|
||||
int shm_id;
|
||||
#endif // ZM_MEM_MAPPED
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
|
||||
volatile SharedData *shared_data;
|
||||
volatile TriggerData *trigger_data;
|
||||
volatile SharedData *shared_data;
|
||||
volatile TriggerData *trigger_data;
|
||||
|
||||
int last_state;
|
||||
int last_event;
|
||||
int last_state;
|
||||
int last_event;
|
||||
|
||||
public:
|
||||
MonitorLink( int p_id, const char *p_name );
|
||||
~MonitorLink();
|
||||
public:
|
||||
MonitorLink( int p_id, const char *p_name );
|
||||
~MonitorLink();
|
||||
|
||||
inline int Id() const
|
||||
{
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const
|
||||
{
|
||||
return( name );
|
||||
}
|
||||
inline int Id() const
|
||||
{
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const
|
||||
{
|
||||
return( name );
|
||||
}
|
||||
|
||||
inline bool isConnected() const
|
||||
{
|
||||
return( connected );
|
||||
}
|
||||
inline time_t getLastConnectTime() const
|
||||
{
|
||||
return( last_connect_time );
|
||||
}
|
||||
inline bool isConnected() const
|
||||
{
|
||||
return( connected );
|
||||
}
|
||||
inline time_t getLastConnectTime() const
|
||||
{
|
||||
return( last_connect_time );
|
||||
}
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
|
||||
bool isAlarmed();
|
||||
bool inAlarm();
|
||||
bool hasAlarmed();
|
||||
};
|
||||
bool isAlarmed();
|
||||
bool inAlarm();
|
||||
bool hasAlarmed();
|
||||
};
|
||||
|
||||
protected:
|
||||
// These are read from the DB and thereafter remain unchanged
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
unsigned int server_id;
|
||||
Function function; // What the monitor is doing
|
||||
bool enabled; // Whether the monitor is enabled or asleep
|
||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||
bool v4l_multi_buffer;
|
||||
unsigned int v4l_captures_per_frame;
|
||||
Orientation orientation; // Whether the image has to be rotated at all
|
||||
unsigned int deinterlacing;
|
||||
int brightness; // The statically saved brightness of the camera
|
||||
int contrast; // The statically saved contrast of the camera
|
||||
int hue; // The statically saved hue of the camera
|
||||
int colour; // The statically saved colour of the camera
|
||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||
char label_format[64]; // The format of the timestamp on the images
|
||||
Coord label_coord; // The coordinates of the timestamp on the images
|
||||
int label_size; // Size of the timestamp on the images
|
||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||
// value is pre_event_count + alarm_frame_count - 1
|
||||
int warmup_count; // How many images to process before looking for events
|
||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||
int section_length; // How long events should last in continuous modes
|
||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||
int frame_skip; // How many frames to skip in continuous modes
|
||||
int motion_frame_skip; // How many frames to skip in motion detection
|
||||
double analysis_fps; // Target framerate for video analysis
|
||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||
int capture_delay; // How long we wait between capture frames
|
||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||
bool track_motion; // Whether this monitor tries to track detected motion
|
||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||
// These are read from the DB and thereafter remain unchanged
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
unsigned int server_id;
|
||||
Function function; // What the monitor is doing
|
||||
bool enabled; // Whether the monitor is enabled or asleep
|
||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||
bool v4l_multi_buffer;
|
||||
unsigned int v4l_captures_per_frame;
|
||||
Orientation orientation; // Whether the image has to be rotated at all
|
||||
unsigned int deinterlacing;
|
||||
int brightness; // The statically saved brightness of the camera
|
||||
int contrast; // The statically saved contrast of the camera
|
||||
int hue; // The statically saved hue of the camera
|
||||
int colour; // The statically saved colour of the camera
|
||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||
char label_format[64]; // The format of the timestamp on the images
|
||||
Coord label_coord; // The coordinates of the timestamp on the images
|
||||
int label_size; // Size of the timestamp on the images
|
||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||
// value is pre_event_count + alarm_frame_count - 1
|
||||
int warmup_count; // How many images to process before looking for events
|
||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||
int section_length; // How long events should last in continuous modes
|
||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||
int frame_skip; // How many frames to skip in continuous modes
|
||||
int motion_frame_skip; // How many frames to skip in motion detection
|
||||
double analysis_fps; // Target framerate for video analysis
|
||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||
int capture_delay; // How long we wait between capture frames
|
||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||
bool track_motion; // Whether this monitor tries to track detected motion
|
||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||
|
||||
double fps;
|
||||
Image delta_image;
|
||||
Image ref_image;
|
||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||
Image write_image; // Used when creating snapshot images
|
||||
double fps;
|
||||
Image delta_image;
|
||||
Image ref_image;
|
||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||
Image write_image; // Used when creating snapshot images
|
||||
|
||||
Purpose purpose; // What this monitor has been created to do
|
||||
int event_count;
|
||||
int image_count;
|
||||
int ready_count;
|
||||
int first_alarm_count;
|
||||
int last_alarm_count;
|
||||
int buffer_count;
|
||||
int prealarm_count;
|
||||
State state;
|
||||
time_t start_time;
|
||||
time_t last_fps_time;
|
||||
time_t auto_resume_time;
|
||||
unsigned int last_motion_score;
|
||||
Purpose purpose; // What this monitor has been created to do
|
||||
int event_count;
|
||||
int image_count;
|
||||
int ready_count;
|
||||
int first_alarm_count;
|
||||
int last_alarm_count;
|
||||
int buffer_count;
|
||||
int prealarm_count;
|
||||
State state;
|
||||
time_t start_time;
|
||||
time_t last_fps_time;
|
||||
time_t auto_resume_time;
|
||||
unsigned int last_motion_score;
|
||||
|
||||
EventCloseMode event_close_mode;
|
||||
EventCloseMode event_close_mode;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
int map_fd;
|
||||
char mem_file[PATH_MAX];
|
||||
#else // ZM_MEM_MAPPED
|
||||
int shm_id;
|
||||
int shm_id;
|
||||
#endif // ZM_MEM_MAPPED
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
off_t mem_size;
|
||||
unsigned char *mem_ptr;
|
||||
|
||||
SharedData *shared_data;
|
||||
TriggerData *trigger_data;
|
||||
SharedData *shared_data;
|
||||
TriggerData *trigger_data;
|
||||
|
||||
Snapshot *image_buffer;
|
||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||
Snapshot *pre_event_buffer;
|
||||
Snapshot *image_buffer;
|
||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||
Snapshot *pre_event_buffer;
|
||||
|
||||
Camera *camera;
|
||||
Camera *camera;
|
||||
|
||||
Event *event;
|
||||
Event *event;
|
||||
|
||||
int n_zones;
|
||||
Zone **zones;
|
||||
int n_zones;
|
||||
Zone **zones;
|
||||
|
||||
struct timeval **timestamps;
|
||||
Image **images;
|
||||
struct timeval **timestamps;
|
||||
Image **images;
|
||||
|
||||
const unsigned char *privacy_bitmask;
|
||||
const unsigned char *privacy_bitmask;
|
||||
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
|
||||
public:
|
||||
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
||||
Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
||||
~Monitor();
|
||||
Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
||||
~Monitor();
|
||||
|
||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||
|
||||
bool connect();
|
||||
inline int ShmValid() const
|
||||
{
|
||||
return( shared_data->valid );
|
||||
}
|
||||
bool connect();
|
||||
inline int ShmValid() const
|
||||
{
|
||||
return( shared_data->valid );
|
||||
}
|
||||
|
||||
inline int Id() const
|
||||
{
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const
|
||||
{
|
||||
return( name );
|
||||
}
|
||||
inline Function GetFunction() const
|
||||
{
|
||||
return( function );
|
||||
}
|
||||
inline bool Enabled()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled );
|
||||
}
|
||||
inline const char *EventPrefix() const
|
||||
{
|
||||
return( event_prefix );
|
||||
}
|
||||
inline bool Ready()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( image_count > ready_count );
|
||||
}
|
||||
inline bool Active()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled && shared_data->active );
|
||||
}
|
||||
inline bool Exif()
|
||||
{
|
||||
return( embed_exif );
|
||||
}
|
||||
inline int Id() const
|
||||
{
|
||||
return( id );
|
||||
}
|
||||
inline const char *Name() const
|
||||
{
|
||||
return( name );
|
||||
}
|
||||
inline Function GetFunction() const
|
||||
{
|
||||
return( function );
|
||||
}
|
||||
inline bool Enabled()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled );
|
||||
}
|
||||
inline const char *EventPrefix() const
|
||||
{
|
||||
return( event_prefix );
|
||||
}
|
||||
inline bool Ready()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( image_count > ready_count );
|
||||
}
|
||||
inline bool Active()
|
||||
{
|
||||
if ( function <= MONITOR )
|
||||
return( false );
|
||||
return( enabled && shared_data->active );
|
||||
}
|
||||
inline bool Exif()
|
||||
{
|
||||
return( embed_exif );
|
||||
}
|
||||
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( camera->Colours() ); }
|
||||
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
||||
unsigned int Width() const { return( width ); }
|
||||
unsigned int Height() const { return( height ); }
|
||||
unsigned int Colours() const { return( camera->Colours() ); }
|
||||
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
||||
|
||||
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 );
|
||||
struct timeval GetTimestamp( int index=-1 ) const;
|
||||
void UpdateAdaptiveSkip();
|
||||
useconds_t GetAnalysisRate();
|
||||
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
||||
int GetCaptureDelay() const { return( capture_delay ); }
|
||||
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
||||
unsigned int GetLastReadIndex() const;
|
||||
unsigned int GetLastWriteIndex() const;
|
||||
unsigned int GetLastEvent() const;
|
||||
double GetFPS() const;
|
||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||
void ForceAlarmOff();
|
||||
void CancelForced();
|
||||
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 );
|
||||
struct timeval GetTimestamp( int index=-1 ) const;
|
||||
void UpdateAdaptiveSkip();
|
||||
useconds_t GetAnalysisRate();
|
||||
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
||||
int GetCaptureDelay() const { return( capture_delay ); }
|
||||
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
||||
unsigned int GetLastReadIndex() const;
|
||||
unsigned int GetLastWriteIndex() const;
|
||||
unsigned int GetLastEvent() const;
|
||||
double GetFPS() const;
|
||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||
void ForceAlarmOff();
|
||||
void CancelForced();
|
||||
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
||||
|
||||
void actionReload();
|
||||
void actionEnable();
|
||||
void actionDisable();
|
||||
void actionSuspend();
|
||||
void actionResume();
|
||||
void actionReload();
|
||||
void actionEnable();
|
||||
void actionDisable();
|
||||
void actionSuspend();
|
||||
void actionResume();
|
||||
|
||||
int actionBrightness( int p_brightness=-1 );
|
||||
int actionHue( int p_hue=-1 );
|
||||
int actionColour( int p_colour=-1 );
|
||||
int actionContrast( int p_contrast=-1 );
|
||||
int actionBrightness( int p_brightness=-1 );
|
||||
int actionHue( int p_hue=-1 );
|
||||
int actionColour( int p_colour=-1 );
|
||||
int actionContrast( int p_contrast=-1 );
|
||||
|
||||
inline int PrimeCapture()
|
||||
{
|
||||
return( camera->PrimeCapture() );
|
||||
}
|
||||
inline int PreCapture()
|
||||
{
|
||||
return( camera->PreCapture() );
|
||||
}
|
||||
int Capture();
|
||||
int PostCapture()
|
||||
{
|
||||
return( camera->PostCapture() );
|
||||
}
|
||||
inline int PrimeCapture()
|
||||
{
|
||||
return( camera->PrimeCapture() );
|
||||
}
|
||||
inline int PreCapture()
|
||||
{
|
||||
return( camera->PreCapture() );
|
||||
}
|
||||
int Capture();
|
||||
int PostCapture()
|
||||
{
|
||||
return( camera->PostCapture() );
|
||||
}
|
||||
|
||||
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
void DumpImage( Image *dump_image ) const;
|
||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||
bool closeEvent();
|
||||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
void DumpImage( Image *dump_image ) const;
|
||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||
bool closeEvent();
|
||||
|
||||
void Reload();
|
||||
void ReloadZones();
|
||||
void ReloadLinkedMonitors( const char * );
|
||||
void Reload();
|
||||
void ReloadZones();
|
||||
void ReloadLinkedMonitors( const char * );
|
||||
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
void DumpZoneImage( const char *zone_string=0 );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
void DumpZoneImage( const char *zone_string=0 );
|
||||
|
||||
#if ZM_HAS_V4L
|
||||
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
||||
#endif // ZM_HAS_V4L
|
||||
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
#if HAVE_LIBAVFORMAT
|
||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
void SingleImageZip( int scale=100 );
|
||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
void SingleImageZip( int scale=100 );
|
||||
#if HAVE_LIBAVCODEC
|
||||
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
};
|
||||
|
||||
|
@ -448,51 +448,51 @@ public:
|
|||
class MonitorStream : public StreamBase
|
||||
{
|
||||
protected:
|
||||
typedef struct SwapImage {
|
||||
bool valid;
|
||||
struct timeval timestamp;
|
||||
char file_name[PATH_MAX];
|
||||
} SwapImage;
|
||||
typedef struct SwapImage {
|
||||
bool valid;
|
||||
struct timeval timestamp;
|
||||
char file_name[PATH_MAX];
|
||||
} SwapImage;
|
||||
|
||||
private:
|
||||
SwapImage *temp_image_buffer;
|
||||
int temp_image_buffer_count;
|
||||
int temp_read_index;
|
||||
int temp_write_index;
|
||||
SwapImage *temp_image_buffer;
|
||||
int temp_image_buffer_count;
|
||||
int temp_read_index;
|
||||
int temp_write_index;
|
||||
|
||||
protected:
|
||||
time_t ttl;
|
||||
time_t ttl;
|
||||
|
||||
protected:
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
|
||||
int frame_count;
|
||||
int frame_count;
|
||||
|
||||
protected:
|
||||
bool checkSwapPath( const char *path, bool create_path );
|
||||
bool checkSwapPath( const char *path, bool create_path );
|
||||
|
||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||
void processCommand( const CmdMsg *msg );
|
||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||
void processCommand( const CmdMsg *msg );
|
||||
|
||||
public:
|
||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 )
|
||||
{
|
||||
}
|
||||
void setStreamBuffer( int p_playback_buffer )
|
||||
{
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
void setStreamTTL( time_t p_ttl )
|
||||
{
|
||||
ttl = p_ttl;
|
||||
}
|
||||
bool setStreamStart( int monitor_id )
|
||||
{
|
||||
return loadMonitor( monitor_id );
|
||||
}
|
||||
void runStream();
|
||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 )
|
||||
{
|
||||
}
|
||||
void setStreamBuffer( int p_playback_buffer )
|
||||
{
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
void setStreamTTL( time_t p_ttl )
|
||||
{
|
||||
ttl = p_ttl;
|
||||
}
|
||||
bool setStreamStart( int monitor_id )
|
||||
{
|
||||
return loadMonitor( monitor_id );
|
||||
}
|
||||
void runStream();
|
||||
};
|
||||
|
||||
#endif // ZM_MONITOR_H
|
||||
|
|
1090
src/zm_mpeg.cpp
1090
src/zm_mpeg.cpp
File diff suppressed because it is too large
Load Diff
|
@ -27,60 +27,60 @@
|
|||
class VideoStream
|
||||
{
|
||||
protected:
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
struct MimeData
|
||||
{
|
||||
const char *format;
|
||||
const char *mime_type;
|
||||
};
|
||||
|
||||
protected:
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
static bool initialised;
|
||||
static struct MimeData mime_data[];
|
||||
|
||||
protected:
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
char *codec_and_format;
|
||||
const char *filename;
|
||||
const char *format;
|
||||
const char *codec_name;
|
||||
enum _AVPIXELFORMAT pf;
|
||||
AVOutputFormat *of;
|
||||
AVFormatContext *ofc;
|
||||
AVStream *ost;
|
||||
AVCodec *codec;
|
||||
AVFrame *opicture;
|
||||
AVFrame *tmp_opicture;
|
||||
uint8_t *video_outbuf;
|
||||
int video_outbuf_size;
|
||||
double last_pts;
|
||||
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
pthread_t streaming_thread;
|
||||
bool do_streaming;
|
||||
uint8_t *buffer_copy;
|
||||
bool add_timestamp;
|
||||
unsigned int timestamp;
|
||||
pthread_mutex_t *buffer_copy_lock;
|
||||
int buffer_copy_size;
|
||||
int buffer_copy_used;
|
||||
AVPacket** packet_buffers;
|
||||
int packet_index;
|
||||
int SendPacket(AVPacket *packet);
|
||||
static void* StreamingThreadCallback(void *ctx);
|
||||
|
||||
protected:
|
||||
static void Initialise();
|
||||
static void Initialise();
|
||||
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
void SetupFormat( );
|
||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||
void SetParameters();
|
||||
void ActuallyOpenStream();
|
||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
|
||||
public:
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||
~VideoStream();
|
||||
const char *MimeType() const;
|
||||
void OpenStream();
|
||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
140
src/zm_poly.cpp
140
src/zm_poly.cpp
|
@ -28,95 +28,95 @@
|
|||
|
||||
void Polygon::calcArea()
|
||||
{
|
||||
double float_area = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
double float_area = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||
float_area += trap_area;
|
||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||
}
|
||||
area = (int)round(fabs(float_area));
|
||||
}
|
||||
|
||||
void Polygon::calcCentre()
|
||||
{
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||
}
|
||||
float_x /= (6*area);
|
||||
float_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
if ( !area && n_coords )
|
||||
calcArea();
|
||||
double float_x = 0.0L, float_y = 0.0L;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||
}
|
||||
float_x /= (6*area);
|
||||
float_y /= (6*area);
|
||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||
}
|
||||
|
||||
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
coords = new Coord[n_coords];
|
||||
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
calcCentre();
|
||||
int min_x = -1;
|
||||
int max_x = -1;
|
||||
int min_y = -1;
|
||||
int max_y = -1;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_coords[i];
|
||||
if ( min_x == -1 || coords[i].X() < min_x )
|
||||
min_x = coords[i].X();
|
||||
if ( max_x == -1 || coords[i].X() > max_x )
|
||||
max_x = coords[i].X();
|
||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||
min_y = coords[i].Y();
|
||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||
max_y = coords[i].Y();
|
||||
}
|
||||
extent = Box( min_x, min_y, max_x, max_y );
|
||||
calcArea();
|
||||
calcCentre();
|
||||
}
|
||||
|
||||
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
||||
{
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
coords = new Coord[n_coords];
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
}
|
||||
|
||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||
{
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
if ( n_coords < p_polygon.n_coords )
|
||||
{
|
||||
delete[] coords;
|
||||
coords = new Coord[p_polygon.n_coords];
|
||||
}
|
||||
n_coords = p_polygon.n_coords;
|
||||
for( int i = 0; i < n_coords; i++ )
|
||||
{
|
||||
coords[i] = p_polygon.coords[i];
|
||||
}
|
||||
extent = p_polygon.extent;
|
||||
area = p_polygon.area;
|
||||
centre = p_polygon.centre;
|
||||
return( *this );
|
||||
}
|
||||
|
||||
bool Polygon::isInside( const Coord &coord ) const
|
||||
{
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
bool inside = false;
|
||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||
{
|
||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return( inside );
|
||||
}
|
||||
|
|
148
src/zm_poly.h
148
src/zm_poly.h
|
@ -33,93 +33,93 @@
|
|||
class Polygon
|
||||
{
|
||||
protected:
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
struct Edge
|
||||
{
|
||||
int min_y;
|
||||
int max_y;
|
||||
double min_x;
|
||||
double _1_m;
|
||||
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
static int CompareYX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
if ( e1->min_y == e2->min_y )
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
else
|
||||
return( int(e1->min_y - e2->min_y) );
|
||||
}
|
||||
static int CompareX( const void *p1, const void *p2 )
|
||||
{
|
||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||
return( int(e1->min_x - e2->min_x) );
|
||||
}
|
||||
};
|
||||
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
struct Slice
|
||||
{
|
||||
int min_x;
|
||||
int max_x;
|
||||
int n_edges;
|
||||
int *edges;
|
||||
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
Slice()
|
||||
{
|
||||
n_edges = 0;
|
||||
edges = 0;
|
||||
}
|
||||
~Slice()
|
||||
{
|
||||
delete edges;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
int n_coords;
|
||||
Coord *coords;
|
||||
Box extent;
|
||||
int area;
|
||||
Coord centre;
|
||||
Edge *edges;
|
||||
Slice *slices;
|
||||
|
||||
protected:
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
void initialiseEdges();
|
||||
void calcArea();
|
||||
void calcCentre();
|
||||
|
||||
public:
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||
{
|
||||
}
|
||||
Polygon( int p_n_coords, const Coord *p_coords );
|
||||
Polygon( const Polygon &p_polygon );
|
||||
~Polygon()
|
||||
{
|
||||
delete[] coords;
|
||||
}
|
||||
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
Polygon &operator=( const Polygon &p_polygon );
|
||||
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
inline int getNumCoords() const { return( n_coords ); }
|
||||
inline const Coord &getCoord( int index ) const
|
||||
{
|
||||
return( coords[index] );
|
||||
}
|
||||
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
inline const Box &Extent() const { return( extent ); }
|
||||
inline int LoX() const { return( extent.LoX() ); }
|
||||
inline int HiX() const { return( extent.HiX() ); }
|
||||
inline int LoY() const { return( extent.LoY() ); }
|
||||
inline int HiY() const { return( extent.HiY() ); }
|
||||
inline int Width() const { return( extent.Width() ); }
|
||||
inline int Height() const { return( extent.Height() ); }
|
||||
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
inline int Area() const { return( area ); }
|
||||
inline const Coord &Centre() const
|
||||
{
|
||||
return( centre );
|
||||
}
|
||||
bool isInside( const Coord &coord ) const;
|
||||
};
|
||||
|
||||
#endif // ZM_POLY_H
|
||||
|
|
|
@ -26,99 +26,99 @@
|
|||
|
||||
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
||||
{
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
const char *errstr;
|
||||
int erroffset = 0;
|
||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||
{
|
||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||
}
|
||||
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
regextra = pcre_study( regex, 0, &errstr );
|
||||
if ( errstr )
|
||||
{
|
||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||
}
|
||||
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
if ( (ok = (bool)regex) )
|
||||
{
|
||||
match_vectors = new int[3*max_matches];
|
||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||
match_buffers = new char *[max_matches];
|
||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||
match_lengths = new int[max_matches];
|
||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||
match_valid = new bool[max_matches];
|
||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||
}
|
||||
n_matches = 0;
|
||||
}
|
||||
|
||||
RegExpr::~RegExpr()
|
||||
{
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
for ( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
if ( match_buffers[i] )
|
||||
{
|
||||
delete[] match_buffers[i];
|
||||
}
|
||||
}
|
||||
delete[] match_valid;
|
||||
delete[] match_lengths;
|
||||
delete[] match_buffers;
|
||||
delete[] match_vectors;
|
||||
}
|
||||
|
||||
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
||||
{
|
||||
match_string = subject_string;
|
||||
match_string = subject_string;
|
||||
|
||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||
|
||||
if ( n_matches <= 0 )
|
||||
{
|
||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
if ( n_matches <= 0 )
|
||||
{
|
||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||
{
|
||||
Error( "Error %d executing regular expression", n_matches );
|
||||
}
|
||||
return( n_matches = 0 );
|
||||
}
|
||||
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
for( int i = 0; i < max_matches; i++ )
|
||||
{
|
||||
match_valid[i] = false;
|
||||
}
|
||||
return( n_matches );
|
||||
}
|
||||
|
||||
const char *RegExpr::MatchString( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
if ( !match_valid[match_index] )
|
||||
{
|
||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||
if ( match_lengths[match_index] < (match_len+1) )
|
||||
{
|
||||
delete[] match_buffers[match_index];
|
||||
match_buffers[match_index] = new char[match_len+1];
|
||||
match_lengths[match_index] = match_len+1;
|
||||
}
|
||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||
match_buffers[match_index][match_len] = '\0';
|
||||
match_valid[match_index] = true;
|
||||
}
|
||||
return( match_buffers[match_index] );
|
||||
}
|
||||
|
||||
int RegExpr::MatchLength( int match_index ) const
|
||||
{
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
if ( match_index > n_matches )
|
||||
{
|
||||
return( 0 );
|
||||
}
|
||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
|
@ -35,29 +35,29 @@
|
|||
class RegExpr
|
||||
{
|
||||
protected:
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
pcre *regex;
|
||||
pcre_extra *regextra;
|
||||
int max_matches;
|
||||
int *match_vectors;
|
||||
mutable char **match_buffers;
|
||||
int *match_lengths;
|
||||
bool *match_valid;
|
||||
|
||||
protected:
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
const char *match_string;
|
||||
int n_matches;
|
||||
|
||||
protected:
|
||||
bool ok;
|
||||
bool ok;
|
||||
|
||||
public:
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||
~RegExpr();
|
||||
bool Ok() const { return( ok ); }
|
||||
int MatchCount() const { return( n_matches ); }
|
||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||
const char *MatchString( int match_index ) const;
|
||||
int MatchLength( int match_index ) const;
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBPCRE
|
||||
|
|
|
@ -22,66 +22,66 @@
|
|||
#include "zm_utils.h"
|
||||
|
||||
RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
protocol( p_protocol ),
|
||||
host( p_host ),
|
||||
port( p_port ),
|
||||
path( p_path ),
|
||||
hp( 0 )
|
||||
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
protocol( p_protocol ),
|
||||
host( p_host ),
|
||||
port( p_port ),
|
||||
path( p_path ),
|
||||
hp( 0 )
|
||||
{
|
||||
if ( path[0] != '/' )
|
||||
path = '/'+path;
|
||||
if ( path[0] != '/' )
|
||||
path = '/'+path;
|
||||
}
|
||||
|
||||
RemoteCamera::~RemoteCamera()
|
||||
{
|
||||
if(hp != NULL) {
|
||||
freeaddrinfo(hp);
|
||||
hp = NULL;
|
||||
}
|
||||
if(hp != NULL) {
|
||||
freeaddrinfo(hp);
|
||||
hp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteCamera::Initialise()
|
||||
{
|
||||
if( protocol.empty() )
|
||||
Fatal( "No protocol specified for remote camera" );
|
||||
if( protocol.empty() )
|
||||
Fatal( "No protocol specified for remote camera" );
|
||||
|
||||
if( host.empty() )
|
||||
Fatal( "No host specified for remote camera" );
|
||||
if( host.empty() )
|
||||
Fatal( "No host specified for remote camera" );
|
||||
|
||||
if( port.empty() )
|
||||
Fatal( "No port specified for remote camera" );
|
||||
if( port.empty() )
|
||||
Fatal( "No port specified for remote camera" );
|
||||
|
||||
//if( path.empty() )
|
||||
//Fatal( "No path specified for remote camera" );
|
||||
//if( path.empty() )
|
||||
//Fatal( "No path specified for remote camera" );
|
||||
|
||||
// Cache as much as we can to speed things up
|
||||
std::string::size_type authIndex = host.rfind( '@' );
|
||||
// Cache as much as we can to speed things up
|
||||
std::string::size_type authIndex = host.rfind( '@' );
|
||||
|
||||
if ( authIndex != std::string::npos )
|
||||
{
|
||||
auth = host.substr( 0, authIndex );
|
||||
host.erase( 0, authIndex+1 );
|
||||
auth64 = base64Encode( auth );
|
||||
if ( authIndex != std::string::npos )
|
||||
{
|
||||
auth = host.substr( 0, authIndex );
|
||||
host.erase( 0, authIndex+1 );
|
||||
auth64 = base64Encode( auth );
|
||||
|
||||
authIndex = auth.rfind( ':' );
|
||||
username = auth.substr(0,authIndex);
|
||||
password = auth.substr( authIndex+1, auth.length() );
|
||||
authIndex = auth.rfind( ':' );
|
||||
username = auth.substr(0,authIndex);
|
||||
password = auth.substr( authIndex+1, auth.length() );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mNeedAuth = false;
|
||||
mAuthenticator = new zm::Authenticator(username,password);
|
||||
mNeedAuth = false;
|
||||
mAuthenticator = new zm::Authenticator(username,password);
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
||||
if ( ret != 0 )
|
||||
{
|
||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
}
|
||||
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
||||
if ( ret != 0 )
|
||||
{
|
||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,44 +35,44 @@
|
|||
class RemoteCamera : public Camera
|
||||
{
|
||||
protected:
|
||||
std::string protocol;
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string path;
|
||||
std::string auth;
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string auth64;
|
||||
std::string protocol;
|
||||
std::string host;
|
||||
std::string port;
|
||||
std::string path;
|
||||
std::string auth;
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string auth64;
|
||||
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
protected:
|
||||
struct addrinfo *hp;
|
||||
struct addrinfo *hp;
|
||||
|
||||
public:
|
||||
RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
virtual ~RemoteCamera();
|
||||
RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
virtual ~RemoteCamera();
|
||||
|
||||
const std::string &Protocol() const { return( protocol ); }
|
||||
const std::string &Host() const { return( host ); }
|
||||
const std::string &Port() const { return( port ); }
|
||||
const std::string &Path() const { return( path ); }
|
||||
const std::string &Auth() const { return( auth ); }
|
||||
const std::string &Username() const { return( username ); }
|
||||
const std::string &Password() const { return( password ); }
|
||||
const std::string &Protocol() const { return( protocol ); }
|
||||
const std::string &Host() const { return( host ); }
|
||||
const std::string &Port() const { return( port ); }
|
||||
const std::string &Path() const { return( path ); }
|
||||
const std::string &Auth() const { return( auth ); }
|
||||
const std::string &Username() const { return( username ); }
|
||||
const std::string &Password() const { return( password ); }
|
||||
|
||||
virtual void Initialise();
|
||||
virtual void Terminate() = 0;
|
||||
virtual int Connect() = 0;
|
||||
virtual int Disconnect() = 0;
|
||||
virtual int PreCapture() = 0;
|
||||
virtual int Capture( Image &image ) = 0;
|
||||
virtual int PostCapture() = 0;
|
||||
virtual void Initialise();
|
||||
virtual void Terminate() = 0;
|
||||
virtual int Connect() = 0;
|
||||
virtual int Disconnect() = 0;
|
||||
virtual int PreCapture() = 0;
|
||||
virtual int Capture( Image &image ) = 0;
|
||||
virtual int PostCapture() = 0;
|
||||
};
|
||||
|
||||
#endif // ZM_REMOTE_CAMERA_H
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,31 +33,31 @@
|
|||
class RemoteCameraHttp : public RemoteCamera
|
||||
{
|
||||
protected:
|
||||
std::string request;
|
||||
struct timeval timeout;
|
||||
//struct hostent *hp;
|
||||
//struct sockaddr_in sa;
|
||||
int sd;
|
||||
Buffer buffer;
|
||||
enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
|
||||
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
|
||||
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
||||
enum { SIMPLE, REGEXP } method;
|
||||
std::string request;
|
||||
struct timeval timeout;
|
||||
//struct hostent *hp;
|
||||
//struct sockaddr_in sa;
|
||||
int sd;
|
||||
Buffer buffer;
|
||||
enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
|
||||
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
|
||||
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
||||
enum { SIMPLE, REGEXP } method;
|
||||
|
||||
public:
|
||||
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~RemoteCameraHttp();
|
||||
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~RemoteCameraHttp();
|
||||
|
||||
void Initialise();
|
||||
void Terminate() { Disconnect(); }
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int SendRequest();
|
||||
int ReadData( Buffer &buffer, int bytes_expected=0 );
|
||||
int GetResponse();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
void Initialise();
|
||||
void Terminate() { Disconnect(); }
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int SendRequest();
|
||||
int ReadData( Buffer &buffer, int bytes_expected=0 );
|
||||
int GetResponse();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
};
|
||||
|
||||
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
||||
|
|
|
@ -29,343 +29,343 @@
|
|||
#include <sys/socket.h>
|
||||
|
||||
RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
rtsp_describe( p_rtsp_describe ),
|
||||
rtspThread( 0 )
|
||||
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||
rtsp_describe( p_rtsp_describe ),
|
||||
rtspThread( 0 )
|
||||
|
||||
{
|
||||
if ( p_method == "rtpUni" )
|
||||
method = RtspThread::RTP_UNICAST;
|
||||
else if ( p_method == "rtpMulti" )
|
||||
method = RtspThread::RTP_MULTICAST;
|
||||
else if ( p_method == "rtpRtsp" )
|
||||
method = RtspThread::RTP_RTSP;
|
||||
else if ( p_method == "rtpRtspHttp" )
|
||||
method = RtspThread::RTP_RTSP_HTTP;
|
||||
else
|
||||
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
||||
if ( p_method == "rtpUni" )
|
||||
method = RtspThread::RTP_UNICAST;
|
||||
else if ( p_method == "rtpMulti" )
|
||||
method = RtspThread::RTP_MULTICAST;
|
||||
else if ( p_method == "rtpRtsp" )
|
||||
method = RtspThread::RTP_RTSP;
|
||||
else if ( p_method == "rtpRtspHttp" )
|
||||
method = RtspThread::RTP_RTSP_HTTP;
|
||||
else
|
||||
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
||||
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
|
||||
mFormatContext = NULL;
|
||||
mVideoStreamId = -1;
|
||||
mCodecContext = NULL;
|
||||
mCodec = NULL;
|
||||
mRawFrame = NULL;
|
||||
mFrame = NULL;
|
||||
frameCount = 0;
|
||||
mFormatContext = NULL;
|
||||
mVideoStreamId = -1;
|
||||
mCodecContext = NULL;
|
||||
mCodec = NULL;
|
||||
mRawFrame = NULL;
|
||||
mFrame = NULL;
|
||||
frameCount = 0;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
mConvertContext = NULL;
|
||||
#endif
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||
if(colours == ZM_COLOUR_RGB32) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
} else if(colours == ZM_COLOUR_RGB24) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||
} else {
|
||||
Panic("Unexpected colours: %d",colours);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RemoteCameraRtsp::~RemoteCameraRtsp()
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
av_frame_free( &mFrame );
|
||||
av_frame_free( &mRawFrame );
|
||||
av_frame_free( &mFrame );
|
||||
av_frame_free( &mRawFrame );
|
||||
#else
|
||||
av_freep( &mFrame );
|
||||
av_freep( &mRawFrame );
|
||||
av_freep( &mFrame );
|
||||
av_freep( &mRawFrame );
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if ( mConvertContext )
|
||||
{
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
if ( mConvertContext )
|
||||
{
|
||||
sws_freeContext( mConvertContext );
|
||||
mConvertContext = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( mCodecContext )
|
||||
{
|
||||
avcodec_close( mCodecContext );
|
||||
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
||||
}
|
||||
if ( mCodecContext )
|
||||
{
|
||||
avcodec_close( mCodecContext );
|
||||
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
||||
}
|
||||
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
if ( capture )
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteCameraRtsp::Initialise()
|
||||
{
|
||||
RemoteCamera::Initialise();
|
||||
RemoteCamera::Initialise();
|
||||
|
||||
int max_size = width*height*colours;
|
||||
int max_size = width*height*colours;
|
||||
|
||||
buffer.size( max_size );
|
||||
buffer.size( max_size );
|
||||
|
||||
if ( logDebugging() )
|
||||
av_log_set_level( AV_LOG_DEBUG );
|
||||
else
|
||||
av_log_set_level( AV_LOG_QUIET );
|
||||
if ( logDebugging() )
|
||||
av_log_set_level( AV_LOG_DEBUG );
|
||||
else
|
||||
av_log_set_level( AV_LOG_QUIET );
|
||||
|
||||
av_register_all();
|
||||
av_register_all();
|
||||
|
||||
Connect();
|
||||
Connect();
|
||||
}
|
||||
|
||||
void RemoteCameraRtsp::Terminate()
|
||||
{
|
||||
Disconnect();
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::Connect()
|
||||
{
|
||||
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
||||
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
||||
|
||||
rtspThread->start();
|
||||
rtspThread->start();
|
||||
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::Disconnect()
|
||||
{
|
||||
if ( rtspThread )
|
||||
{
|
||||
rtspThread->stop();
|
||||
rtspThread->join();
|
||||
delete rtspThread;
|
||||
rtspThread = 0;
|
||||
}
|
||||
return( 0 );
|
||||
if ( rtspThread )
|
||||
{
|
||||
rtspThread->stop();
|
||||
rtspThread->join();
|
||||
delete rtspThread;
|
||||
rtspThread = 0;
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::PrimeCapture()
|
||||
{
|
||||
Debug( 2, "Waiting for sources" );
|
||||
for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ )
|
||||
{
|
||||
usleep( 100000 );
|
||||
}
|
||||
if ( !rtspThread->hasSources() )
|
||||
Fatal( "No RTSP sources" );
|
||||
Debug( 2, "Waiting for sources" );
|
||||
for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ )
|
||||
{
|
||||
usleep( 100000 );
|
||||
}
|
||||
if ( !rtspThread->hasSources() )
|
||||
Fatal( "No RTSP sources" );
|
||||
|
||||
Debug( 2, "Got sources" );
|
||||
Debug( 2, "Got sources" );
|
||||
|
||||
mFormatContext = rtspThread->getFormatContext();
|
||||
mFormatContext = rtspThread->getFormatContext();
|
||||
|
||||
// Find first video stream present
|
||||
mVideoStreamId = -1;
|
||||
// Find first video stream present
|
||||
mVideoStreamId = -1;
|
||||
|
||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||
#else
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||
#endif
|
||||
{
|
||||
mVideoStreamId = i;
|
||||
break;
|
||||
}
|
||||
if ( mVideoStreamId == -1 )
|
||||
Fatal( "Unable to locate video stream" );
|
||||
{
|
||||
mVideoStreamId = i;
|
||||
break;
|
||||
}
|
||||
if ( mVideoStreamId == -1 )
|
||||
Fatal( "Unable to locate video stream" );
|
||||
|
||||
// Get a pointer to the codec context for the video stream
|
||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||
// Get a pointer to the codec context for the video stream
|
||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||
|
||||
// Find the decoder for the video stream
|
||||
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
||||
if ( mCodec == NULL )
|
||||
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
||||
// Find the decoder for the video stream
|
||||
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
||||
if ( mCodec == NULL )
|
||||
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
||||
|
||||
// Open codec
|
||||
// Open codec
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||
#else
|
||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||
#endif
|
||||
Panic( "Can't open codec" );
|
||||
Panic( "Can't open codec" );
|
||||
|
||||
// Allocate space for the native video frame
|
||||
// Allocate space for the native video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mRawFrame = av_frame_alloc();
|
||||
mRawFrame = av_frame_alloc();
|
||||
#else
|
||||
mRawFrame = avcodec_alloc_frame();
|
||||
mRawFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
// Allocate space for the converted video frame
|
||||
// Allocate space for the converted video frame
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||
mFrame = av_frame_alloc();
|
||||
mFrame = av_frame_alloc();
|
||||
#else
|
||||
mFrame = avcodec_alloc_frame();
|
||||
mFrame = avcodec_alloc_frame();
|
||||
#endif
|
||||
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame(s)");
|
||||
if(mRawFrame == NULL || mFrame == NULL)
|
||||
Fatal( "Unable to allocate frame(s)");
|
||||
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||
if( (unsigned int)pSize != imagesize) {
|
||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||
}
|
||||
/*
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
||||
}
|
||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
||||
}
|
||||
|
||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
}
|
||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||
}
|
||||
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
*/
|
||||
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::PreCapture()
|
||||
{
|
||||
if ( !rtspThread->isRunning() )
|
||||
return( -1 );
|
||||
if ( !rtspThread->hasSources() )
|
||||
{
|
||||
Error( "Cannot precapture, no RTP sources" );
|
||||
return( -1 );
|
||||
}
|
||||
return( 0 );
|
||||
if ( !rtspThread->isRunning() )
|
||||
return( -1 );
|
||||
if ( !rtspThread->hasSources() )
|
||||
{
|
||||
Error( "Cannot precapture, no RTP sources" );
|
||||
return( -1 );
|
||||
}
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::Capture( Image &image )
|
||||
{
|
||||
AVPacket packet;
|
||||
uint8_t* directbuffer;
|
||||
int frameComplete = false;
|
||||
AVPacket packet;
|
||||
uint8_t* directbuffer;
|
||||
int frameComplete = false;
|
||||
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if(directbuffer == NULL) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
return (-1);
|
||||
}
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if(directbuffer == NULL) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
while ( true )
|
||||
while ( true )
|
||||
{
|
||||
buffer.clear();
|
||||
if ( !rtspThread->isRunning() )
|
||||
return (-1);
|
||||
|
||||
if ( rtspThread->getFrame( buffer ) )
|
||||
{
|
||||
buffer.clear();
|
||||
if ( !rtspThread->isRunning() )
|
||||
return (-1);
|
||||
Debug( 3, "Read frame %d bytes", buffer.size() );
|
||||
Debug( 4, "Address %p", buffer.head() );
|
||||
Hexdump( 4, buffer.head(), 16 );
|
||||
|
||||
if ( rtspThread->getFrame( buffer ) )
|
||||
if ( !buffer.size() )
|
||||
return( -1 );
|
||||
|
||||
if(mCodecContext->codec_id == AV_CODEC_ID_H264)
|
||||
{
|
||||
// SPS and PPS frames should be saved and appended to IDR frames
|
||||
int nalType = (buffer.head()[3] & 0x1f);
|
||||
|
||||
// SPS
|
||||
if(nalType == 7)
|
||||
{
|
||||
Debug( 3, "Read frame %d bytes", buffer.size() );
|
||||
Debug( 4, "Address %p", buffer.head() );
|
||||
Hexdump( 4, buffer.head(), 16 );
|
||||
lastSps = buffer;
|
||||
continue;
|
||||
}
|
||||
// PPS
|
||||
else if(nalType == 8)
|
||||
{
|
||||
lastPps = buffer;
|
||||
continue;
|
||||
}
|
||||
// IDR
|
||||
else if(nalType == 5)
|
||||
{
|
||||
buffer += lastSps;
|
||||
buffer += lastPps;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !buffer.size() )
|
||||
return( -1 );
|
||||
av_init_packet( &packet );
|
||||
|
||||
if(mCodecContext->codec_id == AV_CODEC_ID_H264)
|
||||
{
|
||||
// SPS and PPS frames should be saved and appended to IDR frames
|
||||
int nalType = (buffer.head()[3] & 0x1f);
|
||||
|
||||
// SPS
|
||||
if(nalType == 7)
|
||||
{
|
||||
lastSps = buffer;
|
||||
continue;
|
||||
}
|
||||
// PPS
|
||||
else if(nalType == 8)
|
||||
{
|
||||
lastPps = buffer;
|
||||
continue;
|
||||
}
|
||||
// IDR
|
||||
else if(nalType == 5)
|
||||
{
|
||||
buffer += lastSps;
|
||||
buffer += lastPps;
|
||||
}
|
||||
}
|
||||
|
||||
av_init_packet( &packet );
|
||||
|
||||
while ( !frameComplete && buffer.size() > 0 )
|
||||
{
|
||||
packet.data = buffer.head();
|
||||
packet.size = buffer.size();
|
||||
while ( !frameComplete && buffer.size() > 0 )
|
||||
{
|
||||
packet.data = buffer.head();
|
||||
packet.size = buffer.size();
|
||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
||||
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
||||
#else
|
||||
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
||||
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
||||
#endif
|
||||
if ( len < 0 )
|
||||
{
|
||||
Error( "Error while decoding frame %d", frameCount );
|
||||
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
||||
buffer.clear();
|
||||
continue;
|
||||
}
|
||||
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
||||
//if ( buffer.size() < 400 )
|
||||
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||
if ( len < 0 )
|
||||
{
|
||||
Error( "Error while decoding frame %d", frameCount );
|
||||
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
||||
buffer.clear();
|
||||
continue;
|
||||
}
|
||||
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
||||
//if ( buffer.size() < 400 )
|
||||
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||
|
||||
buffer -= len;
|
||||
|
||||
}
|
||||
if ( frameComplete ) {
|
||||
|
||||
Debug( 3, "Got frame %d", frameCount );
|
||||
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(mConvertContext == NULL) {
|
||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||
|
||||
if(mConvertContext == NULL)
|
||||
Fatal( "Unable to create conversion context");
|
||||
}
|
||||
|
||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
frameCount++;
|
||||
|
||||
} /* frame complete */
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||
av_packet_unref( &packet);
|
||||
#else
|
||||
av_free_packet( &packet );
|
||||
#endif
|
||||
} /* getFrame() */
|
||||
|
||||
if(frameComplete)
|
||||
return (0);
|
||||
buffer -= len;
|
||||
|
||||
}
|
||||
return (0) ;
|
||||
if ( frameComplete ) {
|
||||
|
||||
Debug( 3, "Got frame %d", frameCount );
|
||||
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
if(mConvertContext == NULL) {
|
||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||
|
||||
if(mConvertContext == NULL)
|
||||
Fatal( "Unable to create conversion context");
|
||||
}
|
||||
|
||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||
#else // HAVE_LIBSWSCALE
|
||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||
#endif // HAVE_LIBSWSCALE
|
||||
|
||||
frameCount++;
|
||||
|
||||
} /* frame complete */
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||
av_packet_unref( &packet);
|
||||
#else
|
||||
av_free_packet( &packet );
|
||||
#endif
|
||||
} /* getFrame() */
|
||||
|
||||
if(frameComplete)
|
||||
return (0);
|
||||
|
||||
}
|
||||
return (0) ;
|
||||
}
|
||||
|
||||
int RemoteCameraRtsp::PostCapture()
|
||||
{
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -35,50 +35,50 @@
|
|||
class RemoteCameraRtsp : public RemoteCamera
|
||||
{
|
||||
protected:
|
||||
struct sockaddr_in rtsp_sa;
|
||||
struct sockaddr_in rtcp_sa;
|
||||
int rtsp_sd;
|
||||
int rtp_sd;
|
||||
int rtcp_sd;
|
||||
bool rtsp_describe;
|
||||
struct sockaddr_in rtsp_sa;
|
||||
struct sockaddr_in rtcp_sa;
|
||||
int rtsp_sd;
|
||||
int rtp_sd;
|
||||
int rtcp_sd;
|
||||
bool rtsp_describe;
|
||||
|
||||
Buffer buffer;
|
||||
Buffer lastSps;
|
||||
Buffer lastPps;
|
||||
Buffer buffer;
|
||||
Buffer lastSps;
|
||||
Buffer lastPps;
|
||||
|
||||
RtspThread::RtspMethod method;
|
||||
RtspThread::RtspMethod method;
|
||||
|
||||
RtspThread *rtspThread;
|
||||
RtspThread *rtspThread;
|
||||
|
||||
int frameCount;
|
||||
int frameCount;
|
||||
|
||||
#if HAVE_LIBAVFORMAT
|
||||
AVFormatContext *mFormatContext;
|
||||
int mVideoStreamId;
|
||||
AVCodecContext *mCodecContext;
|
||||
AVCodec *mCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
AVFormatContext *mFormatContext;
|
||||
int mVideoStreamId;
|
||||
AVCodecContext *mCodecContext;
|
||||
AVCodec *mCodec;
|
||||
AVFrame *mRawFrame;
|
||||
AVFrame *mFrame;
|
||||
_AVPIXELFORMAT imagePixFormat;
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
struct SwsContext *mConvertContext;
|
||||
struct SwsContext *mConvertContext;
|
||||
#endif
|
||||
|
||||
public:
|
||||
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~RemoteCameraRtsp();
|
||||
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||
~RemoteCameraRtsp();
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int PrimeCapture();
|
||||
int PreCapture();
|
||||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
|
||||
};
|
||||
|
||||
|
|
180
src/zm_rgb.h
180
src/zm_rgb.h
|
@ -20,80 +20,80 @@
|
|||
#ifndef ZM_RGB_H
|
||||
#define ZM_RGB_H
|
||||
|
||||
typedef uint32_t Rgb; // RGB colour type
|
||||
typedef uint32_t Rgb; // RGB colour type
|
||||
|
||||
#define WHITE 0xff
|
||||
#define WHITE_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
#define WHITE 0xff
|
||||
#define WHITE_R 0xff
|
||||
#define WHITE_G 0xff
|
||||
#define WHITE_B 0xff
|
||||
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
#define BLACK 0x00
|
||||
#define BLACK_R 0x00
|
||||
#define BLACK_G 0x00
|
||||
#define BLACK_B 0x00
|
||||
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#define RGB_TRANSPARENT (0x01000000)
|
||||
#define RGB_WHITE (0x00ffffff)
|
||||
#define RGB_BLACK (0x00000000)
|
||||
#define RGB_RED (0x000000ff)
|
||||
#define RGB_GREEN (0x0000ff00)
|
||||
#define RGB_BLUE (0x00ff0000)
|
||||
#define RGB_ORANGE (0x0000a5ff)
|
||||
#define RGB_PURPLE (0x00800080)
|
||||
#define RGB_TRANSPARENT (0x01000000)
|
||||
|
||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||
|
||||
/* RGB or RGBA macros */
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* BGR or BGRA */
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||
|
||||
/* ARGB */
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
/* ABGR */
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||
|
||||
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||
|
||||
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
||||
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
||||
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||
|
||||
/* ZM colours */
|
||||
#define ZM_COLOUR_RGB32 4
|
||||
|
@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type
|
|||
|
||||
/* A macro to use default subpixel order for a specified colour. */
|
||||
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||
|
||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||
Rgb result;
|
||||
Rgb result;
|
||||
|
||||
switch(p_subpixorder) {
|
||||
switch(p_subpixorder) {
|
||||
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // ZM_RGB_H
|
||||
|
|
|
@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int consumed = 0;
|
||||
int consumed = 0;
|
||||
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
//printf( "C: " );
|
||||
//for ( int i = 0; i < packetLen; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
int ver = rtcpPacket->header.version;
|
||||
int count = rtcpPacket->header.count;
|
||||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
|
||||
switch( pt )
|
||||
switch( pt )
|
||||
{
|
||||
case RTCP_SR :
|
||||
{
|
||||
case RTCP_SR :
|
||||
{
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
if ( len > 1 )
|
||||
{
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES :
|
||||
{
|
||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||
while ( contentLen )
|
||||
{
|
||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||
|
||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||
Debug( 5, "RTCP Item length %d", item->len );
|
||||
switch( item->type )
|
||||
{
|
||||
case RTCP_SDES_CNAME :
|
||||
{
|
||||
std::string cname( item->data, item->len );
|
||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES_END :
|
||||
case RTCP_SDES_NAME :
|
||||
case RTCP_SDES_EMAIL :
|
||||
case RTCP_SDES_PHONE :
|
||||
case RTCP_SDES_LOC :
|
||||
case RTCP_SDES_TOOL :
|
||||
case RTCP_SDES_NOTE :
|
||||
case RTCP_SDES_PRIV :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||
sdesPtr += paddedLen;
|
||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_BYE :
|
||||
{
|
||||
Debug( 5, "RTCP Got BYE" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
{
|
||||
Error( "Received RTCP_RR packet." );
|
||||
return( -1 );
|
||||
}
|
||||
default :
|
||||
{
|
||||
// Ignore unknown packet types. Some cameras do this by design.
|
||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||
break;
|
||||
}
|
||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||
}
|
||||
break;
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
case RTCP_SDES :
|
||||
{
|
||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||
while ( contentLen )
|
||||
{
|
||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||
|
||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||
Debug( 5, "RTCP Item length %d", item->len );
|
||||
switch( item->type )
|
||||
{
|
||||
case RTCP_SDES_CNAME :
|
||||
{
|
||||
std::string cname( item->data, item->len );
|
||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||
break;
|
||||
}
|
||||
case RTCP_SDES_END :
|
||||
case RTCP_SDES_NAME :
|
||||
case RTCP_SDES_EMAIL :
|
||||
case RTCP_SDES_PHONE :
|
||||
case RTCP_SDES_LOC :
|
||||
case RTCP_SDES_TOOL :
|
||||
case RTCP_SDES_NOTE :
|
||||
case RTCP_SDES_PRIV :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||
sdesPtr += paddedLen;
|
||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTCP_BYE :
|
||||
{
|
||||
Debug( 5, "RTCP Got BYE" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
{
|
||||
Error( "Received RTCP_RR packet." );
|
||||
return( -1 );
|
||||
}
|
||||
default :
|
||||
{
|
||||
// Ignore unknown packet types. Some cameras do this by design.
|
||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_RR;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
mRtpSource.updateRtcpStats();
|
||||
mRtpSource.updateRtcpStats();
|
||||
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
const std::string &cname = mRtpSource.getCname();
|
||||
const std::string &cname = mRtpSource.getCname();
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_SDES;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
rtcpPacket->header.version = RTP_VERSION;
|
||||
rtcpPacket->header.p = 0;
|
||||
rtcpPacket->header.pt = RTCP_BYE;
|
||||
rtcpPacket->header.count = 1;
|
||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||
|
||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
// rtcp_t *r; /* RTCP header */
|
||||
// rtcp_t *end; /* end of compound RTCP packet */
|
||||
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||
// while (r < end && r->common.version == 2);
|
||||
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
// if (r != end) {
|
||||
// /* something wrong with packet format */
|
||||
// }
|
||||
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
}
|
||||
|
||||
int RtpCtrlThread::run()
|
||||
{
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
}
|
||||
else
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||
Fatal( "Failed to connect RTCP server" );
|
||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||
sendReports = true;
|
||||
}
|
||||
|
||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||
Select select( 10 );
|
||||
select.addReader( &rtpCtrlServer );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
|
||||
time_t last_receive = time(NULL);
|
||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||
|
||||
while ( !mStop && select.wait() >= 0 ) {
|
||||
|
||||
time_t now = time(NULL);
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
timeout = true;
|
||||
continue;
|
||||
} else {
|
||||
//Error( "RTCP timed out" );
|
||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||
continue;
|
||||
//break;
|
||||
}
|
||||
} else {
|
||||
timeout = false;
|
||||
last_receive = time(NULL);
|
||||
}
|
||||
else
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||
Fatal( "Failed to connect RTCP server" );
|
||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||
sendReports = true;
|
||||
}
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||
{
|
||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
|
||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||
Select select( 10 );
|
||||
select.addReader( &rtpCtrlServer );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
|
||||
time_t last_receive = time(NULL);
|
||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||
|
||||
while ( !mStop && select.wait() >= 0 ) {
|
||||
|
||||
time_t now = time(NULL);
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
if ( nBytes )
|
||||
{
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
timeout = true;
|
||||
continue;
|
||||
} else {
|
||||
//Error( "RTCP timed out" );
|
||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||
continue;
|
||||
//break;
|
||||
}
|
||||
} else {
|
||||
timeout = false;
|
||||
last_receive = time(NULL);
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||
{
|
||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( nBytes )
|
||||
{
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} else {
|
||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
if ( sendReports )
|
||||
{
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||
Error( "Unable to send: %s", strerror( errno ) );
|
||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||
}
|
||||
} else {
|
||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "zm_thread.h"
|
||||
|
||||
// Defined in ffmpeg rtp.h
|
||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||
|
||||
// Big-endian mask for version, padding bit and packet type pair
|
||||
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
||||
|
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
typedef enum
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
RTCP_SDES_EMAIL = 3,
|
||||
RTCP_SDES_PHONE = 4,
|
||||
RTCP_SDES_LOC = 5,
|
||||
RTCP_SDES_TOOL = 6,
|
||||
RTCP_SDES_NOTE = 7,
|
||||
RTCP_SDES_PRIV = 8
|
||||
} RtcpSdesType;
|
||||
|
||||
struct RtcpCommonHeader
|
||||
{
|
||||
uint8_t count:5; // varies by packet type
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt; // RTCP packet type
|
||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||
};
|
||||
|
||||
// Reception report block
|
||||
struct RtcpRr
|
||||
{
|
||||
uint32_t ssrcN; // data source being reported
|
||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||
uint32_t jitterN; // interarrival jitter, network order
|
||||
uint32_t lsrN; // last SR packet from this source, network order
|
||||
uint32_t dlsrN; // delay since last SR packet, network order
|
||||
};
|
||||
|
||||
// SDES item
|
||||
struct RtcpSdesItem
|
||||
{
|
||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||
uint8_t len; // length of item (in octets)
|
||||
char data[]; // text, not null-terminated
|
||||
};
|
||||
|
||||
// RTCP packet
|
||||
struct RtcpPacket
|
||||
{
|
||||
RtcpCommonHeader header; // common header
|
||||
union
|
||||
{
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
RTCP_BYE = 203,
|
||||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
// Sender Report (SR)
|
||||
struct Sr
|
||||
{
|
||||
uint32_t ssrcN; // sender generating this report, network order
|
||||
uint32_t ntpSecN; // NTP timestamp, network order
|
||||
uint32_t ntpFracN;
|
||||
uint32_t rtpTsN; // RTP timestamp, network order
|
||||
uint32_t pSentN; // packets sent, network order
|
||||
uint32_t oSentN; // octets sent, network order
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} sr;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
RTCP_SDES_EMAIL = 3,
|
||||
RTCP_SDES_PHONE = 4,
|
||||
RTCP_SDES_LOC = 5,
|
||||
RTCP_SDES_TOOL = 6,
|
||||
RTCP_SDES_NOTE = 7,
|
||||
RTCP_SDES_PRIV = 8
|
||||
} RtcpSdesType;
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
struct RtcpCommonHeader
|
||||
{
|
||||
uint8_t count:5; // varies by packet type
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt; // RTCP packet type
|
||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||
};
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// Reception report block
|
||||
struct RtcpRr
|
||||
{
|
||||
uint32_t ssrcN; // data source being reported
|
||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||
uint32_t jitterN; // interarrival jitter, network order
|
||||
uint32_t lsrN; // last SR packet from this source, network order
|
||||
uint32_t dlsrN; // delay since last SR packet, network order
|
||||
};
|
||||
|
||||
// SDES item
|
||||
struct RtcpSdesItem
|
||||
{
|
||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||
uint8_t len; // length of item (in octets)
|
||||
char data[]; // text, not null-terminated
|
||||
};
|
||||
|
||||
// RTCP packet
|
||||
struct RtcpPacket
|
||||
{
|
||||
RtcpCommonHeader header; // common header
|
||||
union
|
||||
{
|
||||
// Sender Report (SR)
|
||||
struct Sr
|
||||
{
|
||||
uint32_t ssrcN; // sender generating this report, network order
|
||||
uint32_t ntpSecN; // NTP timestamp, network order
|
||||
uint32_t ntpFracN;
|
||||
uint32_t rtpTsN; // RTP timestamp, network order
|
||||
uint32_t pSentN; // packets sent, network order
|
||||
uint32_t oSentN; // octets sent, network order
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} sr;
|
||||
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// BYE
|
||||
struct Bye
|
||||
{
|
||||
uint32_t srcN[]; // list of sources
|
||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||
} bye;
|
||||
} body;
|
||||
};
|
||||
// BYE
|
||||
struct Bye
|
||||
{
|
||||
uint32_t srcN[]; // list of sources
|
||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||
} bye;
|
||||
} body;
|
||||
};
|
||||
|
||||
private:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
int mPort;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_CTRL_H
|
||||
|
|
|
@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
|||
|
||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
||||
{
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
//printf( "D: " );
|
||||
//for ( int i = 0; i < 32; i++ )
|
||||
//printf( "%02x ", (unsigned char)packet[i] );
|
||||
//printf( "\n" );
|
||||
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||
Debug( 5, "P: %d", rtpHeader->p );
|
||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||
return( false );
|
||||
}
|
||||
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||
}
|
||||
|
||||
int RtpDataThread::run()
|
||||
{
|
||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||
|
||||
SockAddrInet localAddr;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
SockAddrInet localAddr;
|
||||
UdpInetServer rtpDataSocket;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||
else
|
||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||
if ( !rtpDataSocket.bind( localAddr ) )
|
||||
Fatal( "Failed to bind RTP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
Select select( 3 );
|
||||
select.addReader( &rtpDataSocket );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
while ( !mStop && select.wait() >= 0 )
|
||||
{
|
||||
if ( mStop )
|
||||
break;
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
{
|
||||
Error( "RTP timed out" );
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
{
|
||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||
{
|
||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
if ( nBytes )
|
||||
{
|
||||
Error( "RTP timed out" );
|
||||
mStop = true;
|
||||
break;
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||
else
|
||||
{
|
||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||
{
|
||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
if ( nBytes )
|
||||
{
|
||||
recvPacket( buffer, nBytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Panic( "Barfed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
rtpDataSocket.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -30,16 +30,16 @@ class RtpSource;
|
|||
|
||||
struct RtpDataHeader
|
||||
{
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
uint8_t cc:4; // CSRC count
|
||||
uint8_t x:1; // header extension flag
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
uint8_t pt:7; // payload type
|
||||
uint8_t m:1; // marker bit
|
||||
uint16_t seqN; // sequence number, network order
|
||||
uint32_t timestampN; // timestamp, network order
|
||||
uint32_t ssrcN; // synchronization source, network order
|
||||
uint32_t csrc[]; // optional CSRC list
|
||||
};
|
||||
|
||||
class RtpDataThread : public Thread
|
||||
|
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
|
|||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
RtspThread &mRtspThread;
|
||||
RtpSource &mRtpSource;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||
int run();
|
||||
|
||||
public:
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTP_DATA_H
|
||||
|
|
|
@ -27,358 +27,358 @@
|
|||
#if HAVE_LIBAVCODEC
|
||||
|
||||
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
|
||||
mId( id ),
|
||||
mSsrc( ssrc ),
|
||||
mLocalHost( localHost ),
|
||||
mRemoteHost( remoteHost ),
|
||||
mRtpClock( rtpClock ),
|
||||
mCodecId( codecId ),
|
||||
mFrame( 65536 ),
|
||||
mFrameCount( 0 ),
|
||||
mFrameGood( true ),
|
||||
mFrameReady( false ),
|
||||
mFrameProcessed( false )
|
||||
mId( id ),
|
||||
mSsrc( ssrc ),
|
||||
mLocalHost( localHost ),
|
||||
mRemoteHost( remoteHost ),
|
||||
mRtpClock( rtpClock ),
|
||||
mCodecId( codecId ),
|
||||
mFrame( 65536 ),
|
||||
mFrameCount( 0 ),
|
||||
mFrameGood( true ),
|
||||
mFrameReady( false ),
|
||||
mFrameProcessed( false )
|
||||
{
|
||||
char hostname[256] = "";
|
||||
gethostname( hostname, sizeof(hostname) );
|
||||
char hostname[256] = "";
|
||||
gethostname( hostname, sizeof(hostname) );
|
||||
|
||||
mCname = stringtf( "zm-%d@%s", mId, hostname );
|
||||
Debug( 3, "RTP CName = %s", mCname.c_str() );
|
||||
mCname = stringtf( "zm-%d@%s", mId, hostname );
|
||||
Debug( 3, "RTP CName = %s", mCname.c_str() );
|
||||
|
||||
init( seq );
|
||||
mMaxSeq = seq - 1;
|
||||
mProbation = MIN_SEQUENTIAL;
|
||||
init( seq );
|
||||
mMaxSeq = seq - 1;
|
||||
mProbation = MIN_SEQUENTIAL;
|
||||
|
||||
mLocalPortChans[0] = localPortBase;
|
||||
mLocalPortChans[1] = localPortBase+1;
|
||||
mLocalPortChans[0] = localPortBase;
|
||||
mLocalPortChans[1] = localPortBase+1;
|
||||
|
||||
mRemotePortChans[0] = remotePortBase;
|
||||
mRemotePortChans[1] = remotePortBase+1;
|
||||
mRemotePortChans[0] = remotePortBase;
|
||||
mRemotePortChans[1] = remotePortBase+1;
|
||||
|
||||
mRtpFactor = mRtpClock;
|
||||
mRtpFactor = mRtpClock;
|
||||
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeNtp = tvZero();
|
||||
mBaseTimeRtp = rtpTime;
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeNtp = tvZero();
|
||||
mBaseTimeRtp = rtpTime;
|
||||
|
||||
mLastSrTimeReal = tvZero();
|
||||
mLastSrTimeNtp = tvZero();
|
||||
mLastSrTimeRtp = 0;
|
||||
mLastSrTimeReal = tvZero();
|
||||
mLastSrTimeNtp = tvZero();
|
||||
mLastSrTimeRtp = 0;
|
||||
|
||||
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
|
||||
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
||||
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
|
||||
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
||||
}
|
||||
|
||||
void RtpSource::init( uint16_t seq )
|
||||
{
|
||||
Debug( 3, "Initialising sequence" );
|
||||
mBaseSeq = seq;
|
||||
mMaxSeq = seq;
|
||||
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
|
||||
mCycles = 0;
|
||||
mReceivedPackets = 0;
|
||||
mReceivedPrior = 0;
|
||||
mExpectedPrior = 0;
|
||||
// other initialization
|
||||
mJitter = 0;
|
||||
mTransit = 0;
|
||||
Debug( 3, "Initialising sequence" );
|
||||
mBaseSeq = seq;
|
||||
mMaxSeq = seq;
|
||||
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
|
||||
mCycles = 0;
|
||||
mReceivedPackets = 0;
|
||||
mReceivedPrior = 0;
|
||||
mExpectedPrior = 0;
|
||||
// other initialization
|
||||
mJitter = 0;
|
||||
mTransit = 0;
|
||||
}
|
||||
|
||||
bool RtpSource::updateSeq( uint16_t seq )
|
||||
{
|
||||
uint16_t uDelta = seq - mMaxSeq;
|
||||
uint16_t uDelta = seq - mMaxSeq;
|
||||
|
||||
// Source is not valid until MIN_SEQUENTIAL packets with
|
||||
// sequential sequence numbers have been received.
|
||||
Debug( 5, "Seq: %d", seq );
|
||||
// Source is not valid until MIN_SEQUENTIAL packets with
|
||||
// sequential sequence numbers have been received.
|
||||
Debug( 5, "Seq: %d", seq );
|
||||
|
||||
if ( mProbation)
|
||||
if ( mProbation)
|
||||
{
|
||||
// packet is in sequence
|
||||
if ( seq == mMaxSeq + 1)
|
||||
{
|
||||
// packet is in sequence
|
||||
if ( seq == mMaxSeq + 1)
|
||||
{
|
||||
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
||||
mProbation--;
|
||||
mMaxSeq = seq;
|
||||
if ( mProbation == 0 )
|
||||
{
|
||||
init( seq );
|
||||
mReceivedPackets++;
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
||||
mProbation = MIN_SEQUENTIAL - 1;
|
||||
mMaxSeq = seq;
|
||||
return( false );
|
||||
}
|
||||
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
||||
mProbation--;
|
||||
mMaxSeq = seq;
|
||||
if ( mProbation == 0 )
|
||||
{
|
||||
init( seq );
|
||||
mReceivedPackets++;
|
||||
return( true );
|
||||
}
|
||||
else if ( uDelta < MAX_DROPOUT )
|
||||
{
|
||||
if ( uDelta == 1 )
|
||||
{
|
||||
Debug( 3, "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
|
||||
// in order, with permissible gap
|
||||
if ( seq < mMaxSeq )
|
||||
{
|
||||
// Sequence number wrapped - count another 64K cycle.
|
||||
mCycles += RTP_SEQ_MOD;
|
||||
}
|
||||
mMaxSeq = seq;
|
||||
}
|
||||
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
|
||||
{
|
||||
Warning( "Packet out of sequence, gap %d", uDelta );
|
||||
// the sequence number made a very large jump
|
||||
if ( seq == mBadSeq )
|
||||
{
|
||||
Debug( 3, "Restarting sequence" );
|
||||
// Two sequential packets -- assume that the other side
|
||||
// restarted without telling us so just re-sync
|
||||
// (i.e., pretend this was the first packet).
|
||||
init( seq );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
||||
// duplicate or reordered packet
|
||||
return( false );
|
||||
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
||||
mProbation = MIN_SEQUENTIAL - 1;
|
||||
mMaxSeq = seq;
|
||||
return( false );
|
||||
}
|
||||
mReceivedPackets++;
|
||||
return( uDelta==1?true:false );
|
||||
return( true );
|
||||
}
|
||||
else if ( uDelta < MAX_DROPOUT )
|
||||
{
|
||||
if ( uDelta == 1 )
|
||||
{
|
||||
Debug( 3, "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
|
||||
// in order, with permissible gap
|
||||
if ( seq < mMaxSeq )
|
||||
{
|
||||
// Sequence number wrapped - count another 64K cycle.
|
||||
mCycles += RTP_SEQ_MOD;
|
||||
}
|
||||
mMaxSeq = seq;
|
||||
}
|
||||
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
|
||||
{
|
||||
Warning( "Packet out of sequence, gap %d", uDelta );
|
||||
// the sequence number made a very large jump
|
||||
if ( seq == mBadSeq )
|
||||
{
|
||||
Debug( 3, "Restarting sequence" );
|
||||
// Two sequential packets -- assume that the other side
|
||||
// restarted without telling us so just re-sync
|
||||
// (i.e., pretend this was the first packet).
|
||||
init( seq );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
||||
// duplicate or reordered packet
|
||||
return( false );
|
||||
}
|
||||
mReceivedPackets++;
|
||||
return( uDelta==1?true:false );
|
||||
}
|
||||
|
||||
void RtpSource::updateJitter( const RtpDataHeader *header )
|
||||
{
|
||||
if ( mRtpFactor > 0 )
|
||||
{
|
||||
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
||||
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
||||
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
||||
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
|
||||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
||||
if ( mRtpFactor > 0 )
|
||||
{
|
||||
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
||||
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
||||
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
||||
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
|
||||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
||||
|
||||
if ( mTransit > 0 )
|
||||
{
|
||||
// Jitter
|
||||
int d = packetTransit - mTransit;
|
||||
Debug( 5, "Jitter D = %d", d );
|
||||
if ( d < 0 )
|
||||
d = -d;
|
||||
//mJitter += (1./16.) * ((double)d - mJitter);
|
||||
mJitter += d - ((mJitter + 8) >> 4);
|
||||
}
|
||||
mTransit = packetTransit;
|
||||
}
|
||||
else
|
||||
if ( mTransit > 0 )
|
||||
{
|
||||
mJitter = 0;
|
||||
// Jitter
|
||||
int d = packetTransit - mTransit;
|
||||
Debug( 5, "Jitter D = %d", d );
|
||||
if ( d < 0 )
|
||||
d = -d;
|
||||
//mJitter += (1./16.) * ((double)d - mJitter);
|
||||
mJitter += d - ((mJitter + 8) >> 4);
|
||||
}
|
||||
Debug( 5, "RTP Jitter: %d", mJitter );
|
||||
mTransit = packetTransit;
|
||||
}
|
||||
else
|
||||
{
|
||||
mJitter = 0;
|
||||
}
|
||||
Debug( 5, "RTP Jitter: %d", mJitter );
|
||||
}
|
||||
|
||||
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
|
||||
{
|
||||
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
||||
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
||||
|
||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||
|
||||
if ( mBaseTimeNtp.tv_sec == 0 )
|
||||
{
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeNtp = ntpTime;
|
||||
mBaseTimeRtp = rtpTime;
|
||||
}
|
||||
else if ( !mRtpClock )
|
||||
{
|
||||
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
|
||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||
|
||||
if ( mBaseTimeNtp.tv_sec == 0 )
|
||||
{
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeNtp = ntpTime;
|
||||
mBaseTimeRtp = rtpTime;
|
||||
}
|
||||
else if ( !mRtpClock )
|
||||
{
|
||||
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
|
||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
|
||||
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
||||
|
||||
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
|
||||
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
||||
//Debug( 5, "Real-diff: %.6f", diffRealTime );
|
||||
Debug( 5, "NTP-diff: %.6f", diffNtpTime );
|
||||
Debug( 5, "RTP-diff: %d", diffRtpTime );
|
||||
|
||||
//Debug( 5, "Real-diff: %.6f", diffRealTime );
|
||||
Debug( 5, "NTP-diff: %.6f", diffNtpTime );
|
||||
Debug( 5, "RTP-diff: %d", diffRtpTime );
|
||||
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
||||
|
||||
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
||||
|
||||
Debug( 5, "RTPfactor: %d", mRtpFactor );
|
||||
}
|
||||
mLastSrTimeNtpSecs = ntpTimeSecs;
|
||||
mLastSrTimeNtpFrac = ntpTimeFrac;
|
||||
mLastSrTimeNtp = ntpTime;
|
||||
mLastSrTimeRtp = rtpTime;
|
||||
Debug( 5, "RTPfactor: %d", mRtpFactor );
|
||||
}
|
||||
mLastSrTimeNtpSecs = ntpTimeSecs;
|
||||
mLastSrTimeNtpFrac = ntpTimeFrac;
|
||||
mLastSrTimeNtp = ntpTime;
|
||||
mLastSrTimeRtp = rtpTime;
|
||||
}
|
||||
|
||||
void RtpSource::updateRtcpStats()
|
||||
{
|
||||
uint32_t extendedMax = mCycles + mMaxSeq;
|
||||
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
||||
uint32_t extendedMax = mCycles + mMaxSeq;
|
||||
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
||||
|
||||
Debug( 5, "Expected packets = %d", mExpectedPackets );
|
||||
Debug( 5, "Expected packets = %d", mExpectedPackets );
|
||||
|
||||
// The number of packets lost is defined to be the number of packets
|
||||
// expected less the number of packets actually received:
|
||||
mLostPackets = mExpectedPackets - mReceivedPackets;
|
||||
Debug( 5, "Lost packets = %d", mLostPackets );
|
||||
// The number of packets lost is defined to be the number of packets
|
||||
// expected less the number of packets actually received:
|
||||
mLostPackets = mExpectedPackets - mReceivedPackets;
|
||||
Debug( 5, "Lost packets = %d", mLostPackets );
|
||||
|
||||
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
||||
Debug( 5, "Expected interval = %d", expectedInterval );
|
||||
mExpectedPrior = mExpectedPackets;
|
||||
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
||||
Debug( 5, "Received interval = %d", receivedInterval );
|
||||
mReceivedPrior = mReceivedPackets;
|
||||
uint32_t lostInterval = expectedInterval - receivedInterval;
|
||||
Debug( 5, "Lost interval = %d", lostInterval );
|
||||
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
||||
Debug( 5, "Expected interval = %d", expectedInterval );
|
||||
mExpectedPrior = mExpectedPackets;
|
||||
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
||||
Debug( 5, "Received interval = %d", receivedInterval );
|
||||
mReceivedPrior = mReceivedPackets;
|
||||
uint32_t lostInterval = expectedInterval - receivedInterval;
|
||||
Debug( 5, "Lost interval = %d", lostInterval );
|
||||
|
||||
if ( expectedInterval == 0 || lostInterval <= 0 )
|
||||
mLostFraction = 0;
|
||||
else
|
||||
mLostFraction = (lostInterval << 8) / expectedInterval;
|
||||
Debug( 5, "Lost fraction = %d", mLostFraction );
|
||||
if ( expectedInterval == 0 || lostInterval <= 0 )
|
||||
mLostFraction = 0;
|
||||
else
|
||||
mLostFraction = (lostInterval << 8) / expectedInterval;
|
||||
Debug( 5, "Lost fraction = %d", mLostFraction );
|
||||
}
|
||||
|
||||
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||
{
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
||||
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
||||
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||
|
||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||
{
|
||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
||||
|
||||
if ( mFrameGood )
|
||||
{
|
||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
||||
int extraHeader = 0;
|
||||
|
||||
if ( mFrameGood )
|
||||
if( mCodecId == AV_CODEC_ID_H264 )
|
||||
{
|
||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||
|
||||
switch (nalType)
|
||||
{
|
||||
int extraHeader = 0;
|
||||
|
||||
if( mCodecId == AV_CODEC_ID_H264 )
|
||||
case 24:
|
||||
{
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
case 25: case 26: case 27:
|
||||
{
|
||||
extraHeader = 3;
|
||||
break;
|
||||
}
|
||||
// FU-A and FU-B
|
||||
case 28: case 29:
|
||||
{
|
||||
// Is this NAL the first NAL in fragmentation sequence
|
||||
if ( packet[rtpHeaderSize+1] & 0x80 )
|
||||
{
|
||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||
|
||||
switch (nalType)
|
||||
{
|
||||
case 24:
|
||||
{
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
case 25: case 26: case 27:
|
||||
{
|
||||
extraHeader = 3;
|
||||
break;
|
||||
}
|
||||
// FU-A and FU-B
|
||||
case 28: case 29:
|
||||
{
|
||||
// Is this NAL the first NAL in fragmentation sequence
|
||||
if ( packet[rtpHeaderSize+1] & 0x80 )
|
||||
{
|
||||
// Now we will form new header of frame
|
||||
mFrame.append( "\x0\x0\x1\x0", 4 );
|
||||
// Reconstruct NAL header from FU headers
|
||||
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
|
||||
(packet[rtpHeaderSize] & 0xe0);
|
||||
}
|
||||
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Append NAL frame start code
|
||||
if ( !mFrame.size() )
|
||||
mFrame.append( "\x0\x0\x1", 3 );
|
||||
// Now we will form new header of frame
|
||||
mFrame.append( "\x0\x0\x1\x0", 4 );
|
||||
// Reconstruct NAL header from FU headers
|
||||
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
|
||||
(packet[rtpHeaderSize] & 0xe0);
|
||||
}
|
||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Hexdump( 4, mFrame.head(), 16 );
|
||||
|
||||
if ( thisM )
|
||||
{
|
||||
if ( mFrameGood )
|
||||
{
|
||||
Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
|
||||
mFrameProcessed.setValueImmediate( false );
|
||||
mFrameReady.updateValueSignal( true );
|
||||
if ( !mFrameProcessed.getValueImmediate() )
|
||||
{
|
||||
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
|
||||
if( count > 1 )
|
||||
return( false );
|
||||
}
|
||||
mFrameCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
mFrame.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mFrame.size() )
|
||||
{
|
||||
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Discarding frame %d", mFrameCount );
|
||||
}
|
||||
mFrameGood = false;
|
||||
mFrame.clear();
|
||||
// Append NAL frame start code
|
||||
if ( !mFrame.size() )
|
||||
mFrame.append( "\x0\x0\x1", 3 );
|
||||
}
|
||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||
}
|
||||
|
||||
Hexdump( 4, mFrame.head(), 16 );
|
||||
|
||||
if ( thisM )
|
||||
{
|
||||
mFrameGood = true;
|
||||
prevM = true;
|
||||
if ( mFrameGood )
|
||||
{
|
||||
Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
|
||||
mFrameProcessed.setValueImmediate( false );
|
||||
mFrameReady.updateValueSignal( true );
|
||||
if ( !mFrameProcessed.getValueImmediate() )
|
||||
{
|
||||
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
|
||||
if( count > 1 )
|
||||
return( false );
|
||||
}
|
||||
mFrameCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
mFrame.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mFrame.size() )
|
||||
{
|
||||
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
else
|
||||
prevM = false;
|
||||
{
|
||||
Warning( "Discarding frame %d", mFrameCount );
|
||||
}
|
||||
mFrameGood = false;
|
||||
mFrame.clear();
|
||||
}
|
||||
if ( thisM )
|
||||
{
|
||||
mFrameGood = true;
|
||||
prevM = true;
|
||||
}
|
||||
else
|
||||
prevM = false;
|
||||
|
||||
updateJitter( rtpHeader );
|
||||
updateJitter( rtpHeader );
|
||||
|
||||
return( true );
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool RtpSource::getFrame( Buffer &buffer )
|
||||
{
|
||||
Debug( 3, "Getting frame" );
|
||||
if ( !mFrameReady.getValueImmediate() )
|
||||
{
|
||||
// Allow for a couple of spurious returns
|
||||
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
|
||||
if ( count > 1 )
|
||||
return( false );
|
||||
}
|
||||
buffer = mFrame;
|
||||
mFrameReady.setValueImmediate( false );
|
||||
mFrameProcessed.updateValueSignal( true );
|
||||
Debug( 3, "Copied %d bytes", buffer.size() );
|
||||
return( true );
|
||||
Debug( 3, "Getting frame" );
|
||||
if ( !mFrameReady.getValueImmediate() )
|
||||
{
|
||||
// Allow for a couple of spurious returns
|
||||
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
|
||||
if ( count > 1 )
|
||||
return( false );
|
||||
}
|
||||
buffer = mFrame;
|
||||
mFrameReady.setValueImmediate( false );
|
||||
mFrameProcessed.updateValueSignal( true );
|
||||
Debug( 3, "Copied %d bytes", buffer.size() );
|
||||
return( true );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
|
@ -35,152 +35,152 @@ struct RtpDataHeader;
|
|||
class RtpSource
|
||||
{
|
||||
public:
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||
private:
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
static const int RTP_SEQ_MOD = 1<<16;
|
||||
static const int MAX_DROPOUT = 3000;
|
||||
static const int MAX_MISORDER = 100;
|
||||
static const int MIN_SEQUENTIAL = 2;
|
||||
|
||||
private:
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
// Identity
|
||||
int mId; // General id (usually monitor id)
|
||||
std::string mCname; // Canonical name, for SDES
|
||||
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
// RTP/RTCP fields
|
||||
uint32_t mSsrc;
|
||||
uint16_t mMaxSeq; // highest seq. number seen
|
||||
uint32_t mCycles; // shifted count of seq. number cycles
|
||||
uint32_t mBaseSeq; // base seq number
|
||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||
uint32_t mProbation; // sequ. packets till source is valid
|
||||
uint32_t mReceivedPackets; // packets received
|
||||
uint32_t mExpectedPrior; // packet expected at last interval
|
||||
uint32_t mReceivedPrior; // packet received at last interval
|
||||
uint32_t mTransit; // relative trans time for prev pkt
|
||||
uint32_t mJitter; // estimated jitter
|
||||
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
// Ports/Channels
|
||||
std::string mLocalHost;
|
||||
int mLocalPortChans[2];
|
||||
std::string mRemoteHost;
|
||||
int mRemotePortChans[2];
|
||||
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
// Time keys
|
||||
uint32_t mRtpClock;
|
||||
uint32_t mRtpFactor;
|
||||
struct timeval mBaseTimeReal;
|
||||
struct timeval mBaseTimeNtp;
|
||||
uint32_t mBaseTimeRtp;
|
||||
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
struct timeval mLastSrTimeReal;
|
||||
uint32_t mLastSrTimeNtpSecs;
|
||||
uint32_t mLastSrTimeNtpFrac;
|
||||
struct timeval mLastSrTimeNtp;
|
||||
uint32_t mLastSrTimeRtp;
|
||||
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
// Stats, intermittently updated
|
||||
uint32_t mExpectedPackets;
|
||||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
|
||||
_AVCODECID mCodecId;
|
||||
_AVCODECID mCodecId;
|
||||
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
|
||||
private:
|
||||
void init( uint16_t seq );
|
||||
void init( uint16_t seq );
|
||||
|
||||
public:
|
||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
void updateRtcpStats();
|
||||
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
void setSsrc( uint32_t ssrc )
|
||||
{
|
||||
mSsrc = ssrc;
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &buffer );
|
||||
bool getFrame( Buffer &buffer );
|
||||
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
const std::string &getCname() const
|
||||
{
|
||||
return( mCname );
|
||||
}
|
||||
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
const std::string &getLocalHost() const
|
||||
{
|
||||
return( mLocalHost );
|
||||
}
|
||||
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
int getLocalDataPort() const
|
||||
{
|
||||
return( mLocalPortChans[0] );
|
||||
}
|
||||
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
int getLocalCtrlPort() const
|
||||
{
|
||||
return( mLocalPortChans[1] );
|
||||
}
|
||||
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
const std::string &getRemoteHost() const
|
||||
{
|
||||
return( mRemoteHost );
|
||||
}
|
||||
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
int getRemoteDataPort() const
|
||||
{
|
||||
return( mRemotePortChans[0] );
|
||||
}
|
||||
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
int getRemoteCtrlPort() const
|
||||
{
|
||||
return( mRemotePortChans[1] );
|
||||
}
|
||||
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
uint32_t getMaxSeq() const
|
||||
{
|
||||
return( mCycles + mMaxSeq );
|
||||
}
|
||||
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
uint32_t getExpectedPackets() const
|
||||
{
|
||||
return( mExpectedPackets );
|
||||
}
|
||||
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
uint32_t getLostPackets() const
|
||||
{
|
||||
return( mLostPackets );
|
||||
}
|
||||
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
uint8_t getLostFraction() const
|
||||
{
|
||||
return( mLostFraction );
|
||||
}
|
||||
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
uint32_t getJitter() const
|
||||
{
|
||||
return( mJitter >> 4 );
|
||||
}
|
||||
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
uint32_t getLastSrTimestamp() const
|
||||
{
|
||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
1508
src/zm_rtsp.cpp
1508
src/zm_rtsp.cpp
File diff suppressed because it is too large
Load Diff
152
src/zm_rtsp.h
152
src/zm_rtsp.h
|
@ -34,110 +34,110 @@
|
|||
class RtspThread : public Thread
|
||||
{
|
||||
public:
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||
|
||||
private:
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
typedef std::set<int> PortSet;
|
||||
typedef std::set<uint32_t> SsrcSet;
|
||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||
|
||||
private:
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
static int smMinDataPort;
|
||||
static int smMaxDataPort;
|
||||
static PortSet smLocalSsrcs;
|
||||
static PortSet smAssignedPorts;
|
||||
|
||||
private:
|
||||
int mId;
|
||||
int mId;
|
||||
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
RtspMethod mMethod;
|
||||
std::string mProtocol;
|
||||
std::string mHost;
|
||||
std::string mPort;
|
||||
std::string mPath;
|
||||
bool mRtspDescribe;
|
||||
std::string mUrl;
|
||||
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
// Reworked authentication system
|
||||
// First try without authentication, even if we have a username and password
|
||||
// on receiving a 401 response, select authentication method (basic or digest)
|
||||
// fill required fields and set needAuth
|
||||
// subsequent requests can set the required authentication header.
|
||||
bool mNeedAuth;
|
||||
int respCode;
|
||||
zm::Authenticator* mAuthenticator;
|
||||
|
||||
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
TcpInetClient mRtspSocket;
|
||||
TcpInetClient mRtspSocket2;
|
||||
|
||||
SourceMap mSources;
|
||||
SourceMap mSources;
|
||||
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
SessionDescriptor *mSessDesc;
|
||||
AVFormatContext *mFormatContext;
|
||||
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
uint16_t mSeq;
|
||||
uint32_t mSession;
|
||||
uint32_t mSsrc;
|
||||
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
int mRemotePorts[2];
|
||||
int mRemoteChannels[2];
|
||||
RtspDist mDist;
|
||||
|
||||
unsigned long mRtpTime;
|
||||
unsigned long mRtpTime;
|
||||
|
||||
bool mStop;
|
||||
bool mStop;
|
||||
|
||||
private:
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
bool sendCommand( std::string message );
|
||||
bool recvResponse( std::string &response );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
public:
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||
~RtspThread();
|
||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||
~RtspThread();
|
||||
|
||||
public:
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
int requestPorts();
|
||||
void releasePorts( int port );
|
||||
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
bool isValidSsrc( uint32_t ssrc );
|
||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
uint32_t getSsrc() const
|
||||
{
|
||||
return( mSsrc );
|
||||
}
|
||||
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
bool hasSources() const
|
||||
{
|
||||
return( !mSources.empty() );
|
||||
}
|
||||
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
AVFormatContext *getFormatContext()
|
||||
{
|
||||
return( mFormatContext );
|
||||
}
|
||||
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
bool getFrame( Buffer &frame )
|
||||
{
|
||||
SourceMap::iterator iter = mSources.begin();
|
||||
if ( iter == mSources.end() )
|
||||
return( false );
|
||||
return( iter->second->getFrame( frame ) );
|
||||
}
|
||||
int run();
|
||||
void stop()
|
||||
{
|
||||
mStop = true;
|
||||
}
|
||||
bool stopped() const
|
||||
{
|
||||
return( mStop );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_RTSP_H
|
||||
|
|
|
@ -28,206 +28,206 @@ namespace zm {
|
|||
|
||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fUsername = username;
|
||||
fPassword = password;
|
||||
nc = 1;
|
||||
fCnonce = "0a4f113b";
|
||||
}
|
||||
|
||||
Authenticator::~Authenticator() {
|
||||
reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
void Authenticator::reset() {
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
fRealm.clear();
|
||||
fNonce.clear();
|
||||
fUsername.clear();
|
||||
fPassword.clear();
|
||||
fAuthMethod = AUTH_UNDEFINED;
|
||||
}
|
||||
|
||||
void Authenticator::authHandleHeader(std::string headerData)
|
||||
{
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
const char* basic_match = "Basic ";
|
||||
const char* digest_match = "Digest ";
|
||||
size_t digest_match_len = strlen(digest_match);
|
||||
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
// Check if basic auth
|
||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_BASIC;
|
||||
Debug( 2, "Set authMethod to Basic");
|
||||
}
|
||||
// Check if digest auth
|
||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_DIGEST;
|
||||
Debug( 2, "Set authMethod to Digest");
|
||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||
// subparts are key="value"
|
||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||
{
|
||||
fAuthMethod = AUTH_BASIC;
|
||||
Debug( 2, "Set authMethod to Basic");
|
||||
}
|
||||
// Check if digest auth
|
||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||
{
|
||||
fAuthMethod = AUTH_DIGEST;
|
||||
Debug( 2, "Set authMethod to Digest");
|
||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||
// subparts are key="value"
|
||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||
{
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
std::string key = trimSpaces( kvPair[0] );
|
||||
if (key == "realm") {
|
||||
fRealm = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "nonce") {
|
||||
fNonce = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "qop") {
|
||||
fQop = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||
std::string key = trimSpaces( kvPair[0] );
|
||||
if (key == "realm") {
|
||||
fRealm = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "nonce") {
|
||||
fNonce = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
if (key == "qop") {
|
||||
fQop = trimSet( kvPair[1], "\"");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
std::string Authenticator::quote(std::string src)
|
||||
{
|
||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||
}
|
||||
|
||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
||||
{
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
std::string result = "Authorization: ";
|
||||
if (fAuthMethod == AUTH_BASIC)
|
||||
{
|
||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||
}
|
||||
else if (fAuthMethod == AUTH_DIGEST)
|
||||
{
|
||||
result += std::string("Digest ") +
|
||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||
if ( ! fQop.empty() ) {
|
||||
result += ", qop=" + fQop;
|
||||
result += ", nc=" + stringtf("%08x",nc);
|
||||
result += ", cnonce=\"" + fCnonce + "\"";
|
||||
}
|
||||
else if (fAuthMethod == AUTH_DIGEST)
|
||||
{
|
||||
result += std::string("Digest ") +
|
||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||
if ( ! fQop.empty() ) {
|
||||
result += ", qop=" + fQop;
|
||||
result += ", nc=" + stringtf("%08x",nc);
|
||||
result += ", cnonce=\"" + fCnonce + "\"";
|
||||
}
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||
result += ", algorithm=\"MD5\"";
|
||||
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
//Authorization: Digest username="zm",
|
||||
// realm="NC-336PW-HD-1080P",
|
||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||
// response="4092120557d3099a163bd51a0d59744d",
|
||||
// algorithm=MD5,
|
||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||
// qop="auth",
|
||||
// cnonce="c8051140765877dc",
|
||||
// nc=00000001
|
||||
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
result += "\r\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
// The "response" field is computed as:
|
||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||
size_t md5len = 16;
|
||||
unsigned char md5buf[md5len];
|
||||
char md5HexBuf[md5len*2+1];
|
||||
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
// Step 1: md5(<username>:<realm>:<password>)
|
||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha1Hash = md5HexBuf;
|
||||
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
// Step 2: md5(<cmd>:<url>)
|
||||
std::string ha2Data = method + ":" + uri;
|
||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
std::string ha2Hash = md5HexBuf;
|
||||
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
// Step 3: md5(ha1:<nonce>:ha2)
|
||||
std::string digestData = ha1Hash + ":" + nonce();
|
||||
if ( ! fQop.empty() ) {
|
||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||
nc ++;
|
||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||
}
|
||||
digestData += ":" + ha2Hash;
|
||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||
#if HAVE_DECL_MD5
|
||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||
#endif
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||
}
|
||||
md5HexBuf[md5len*2]='\0';
|
||||
|
||||
return md5HexBuf;
|
||||
return md5HexBuf;
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||
return( 0 );
|
||||
#endif // HAVE_DECL_MD5
|
||||
}
|
||||
|
||||
void Authenticator::checkAuthResponse(std::string &response) {
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
std::string authLine;
|
||||
StringVector lines = split( response, "\r\n" );
|
||||
const char* authenticate_match = "WWW-Authenticate:";
|
||||
size_t authenticate_match_len = strlen(authenticate_match);
|
||||
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
// stop at end of headers
|
||||
if (lines[i].length()==0)
|
||||
break;
|
||||
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||
authLine = lines[i];
|
||||
Debug( 2, "Found auth line at %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!authLine.empty()) {
|
||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||
} else {
|
||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zm
|
||||
|
|
|
@ -37,19 +37,19 @@ namespace zm {
|
|||
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
||||
class Authenticator {
|
||||
public:
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
Authenticator(std::string &username, std::string password);
|
||||
virtual ~Authenticator();
|
||||
void reset();
|
||||
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
std::string realm() { return fRealm; }
|
||||
std::string nonce() { return fNonce; }
|
||||
std::string username() { return fUsername; }
|
||||
AuthMethod auth_method() const { return fAuthMethod; }
|
||||
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||
void authHandleHeader( std::string headerData );
|
||||
std::string getAuthHeader( std::string method, std::string path );
|
||||
void checkAuthResponse(std::string &response);
|
||||
|
||||
private:
|
||||
std::string password() { return fPassword; }
|
||||
|
@ -61,7 +61,7 @@ private:
|
|||
std::string fUsername;
|
||||
std::string fPassword;
|
||||
std::string quote( std::string src );
|
||||
int nc;
|
||||
int nc;
|
||||
};
|
||||
|
||||
} // namespace zm
|
||||
|
|
824
src/zm_sdp.cpp
824
src/zm_sdp.cpp
|
@ -25,489 +25,489 @@
|
|||
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||
};
|
||||
#else
|
||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||
};
|
||||
|
||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||
};
|
||||
#endif
|
||||
|
||||
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
mTtl( 16 ),
|
||||
mNoAddresses( 0 )
|
||||
{
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
StringVector tokens = split( connInfo, " " );
|
||||
if ( tokens.size() < 3 )
|
||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||
mNetworkType = tokens[0];
|
||||
if ( mNetworkType != "IN" )
|
||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mAddressType = tokens[1];
|
||||
if ( mAddressType != "IP4" )
|
||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||
StringVector addressTokens = split( tokens[2], "/" );
|
||||
if ( addressTokens.size() < 1 )
|
||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||
mAddress = addressTokens[0];
|
||||
if ( addressTokens.size() >= 2 )
|
||||
mTtl = atoi(addressTokens[1].c_str());
|
||||
if ( addressTokens.size() >= 3 )
|
||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
||||
mValue( 0 )
|
||||
mValue( 0 )
|
||||
{
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
StringVector tokens = split( bandInfo, ":" );
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||
mType = tokens[0];
|
||||
//if ( mNetworkType != "IN" )
|
||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||
mValue = atoi(tokens[1].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
mTransport( transport ),
|
||||
mPayloadType( payloadType ),
|
||||
mFrameRate( 0.0 ),
|
||||
mClock( 0 ),
|
||||
mWidth( 0 ),
|
||||
mHeight( 0 ),
|
||||
mSprops( "" ),
|
||||
mConnInfo( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
mUrl( url ),
|
||||
mConnInfo( 0 ),
|
||||
mBandInfo( 0 )
|
||||
{
|
||||
MediaDescriptor *currMedia = 0;
|
||||
MediaDescriptor *currMedia = 0;
|
||||
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||
StringVector lines = split( sdp, "\r\n" );
|
||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||
{
|
||||
std::string line = *iter;
|
||||
if ( line.empty() )
|
||||
break;
|
||||
|
||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||
const char sdpType = line[0];
|
||||
if ( line[1] != '=' )
|
||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||
|
||||
line.erase( 0, 2 );
|
||||
switch( sdpType )
|
||||
{
|
||||
std::string line = *iter;
|
||||
if ( line.empty() )
|
||||
break;
|
||||
|
||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||
const char sdpType = line[0];
|
||||
if ( line[1] != '=' )
|
||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||
|
||||
line.erase( 0, 2 );
|
||||
switch( sdpType )
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
mInfo = line;
|
||||
break;
|
||||
case 'c' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
mConnInfo = new ConnInfo( line );
|
||||
break;
|
||||
case 'b' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
mBandInfo = new BandInfo( line );
|
||||
break;
|
||||
case 't' :
|
||||
mTimeInfo = line;
|
||||
break;
|
||||
case 'a' :
|
||||
{
|
||||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
{
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
case 'o' :
|
||||
mOwner = line;
|
||||
break;
|
||||
case 's' :
|
||||
mName = line;
|
||||
break;
|
||||
case 'i' :
|
||||
mInfo = line;
|
||||
break;
|
||||
case 'c' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
mConnInfo = new ConnInfo( line );
|
||||
break;
|
||||
case 'b' :
|
||||
// This prevent a memory leak if the field appears more than one time
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
mBandInfo = new BandInfo( line );
|
||||
break;
|
||||
case 't' :
|
||||
mTimeInfo = line;
|
||||
break;
|
||||
case 'a' :
|
||||
if ( attrName == "control" )
|
||||
{
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
currMedia->setControlUrl( tokens[1] );
|
||||
}
|
||||
else if ( attrName == "range" )
|
||||
{
|
||||
}
|
||||
else if ( attrName == "rtpmap" )
|
||||
{
|
||||
// a=rtpmap:96 MP4V-ES/90000
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
std::string payloadDesc = attrTokens[1];
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||
std::string payloadDesc = payloadTokens[0];
|
||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||
currMedia->setPayloadDesc( payloadDesc );
|
||||
currMedia->setClock( payloadClock );
|
||||
}
|
||||
}
|
||||
else if ( attrName == "framesize" )
|
||||
{
|
||||
// a=framesize:96 320-240
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
StringVector sizeTokens = split( attrTokens[1], "-" );
|
||||
int width = atoi(sizeTokens[0].c_str());
|
||||
int height = atoi(sizeTokens[1].c_str());
|
||||
currMedia->setFrameSize( width, height );
|
||||
}
|
||||
else if ( attrName == "framerate" )
|
||||
{
|
||||
// a=framerate:5.0
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
double frameRate = atof(tokens[1].c_str());
|
||||
currMedia->setFrameRate( frameRate );
|
||||
}
|
||||
else if ( attrName == "fmtp" )
|
||||
{
|
||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " ", 2 );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||
{
|
||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||
if ( attr3Tokens[0] == "profile-level-id" )
|
||||
{
|
||||
if ( attrName == "control" )
|
||||
{
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
currMedia->setControlUrl( tokens[1] );
|
||||
}
|
||||
else if ( attrName == "range" )
|
||||
{
|
||||
}
|
||||
else if ( attrName == "rtpmap" )
|
||||
{
|
||||
// a=rtpmap:96 MP4V-ES/90000
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
std::string payloadDesc = attrTokens[1];
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||
std::string payloadDesc = payloadTokens[0];
|
||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||
currMedia->setPayloadDesc( payloadDesc );
|
||||
currMedia->setClock( payloadClock );
|
||||
}
|
||||
}
|
||||
else if ( attrName == "framesize" )
|
||||
{
|
||||
// a=framesize:96 320-240
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " " );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
StringVector sizeTokens = split( attrTokens[1], "-" );
|
||||
int width = atoi(sizeTokens[0].c_str());
|
||||
int height = atoi(sizeTokens[1].c_str());
|
||||
currMedia->setFrameSize( width, height );
|
||||
}
|
||||
else if ( attrName == "framerate" )
|
||||
{
|
||||
// a=framerate:5.0
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
double frameRate = atof(tokens[1].c_str());
|
||||
currMedia->setFrameRate( frameRate );
|
||||
}
|
||||
else if ( attrName == "fmtp" )
|
||||
{
|
||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
StringVector attrTokens = split( tokens[1], " ", 2 );
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||
{
|
||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||
if ( attr3Tokens[0] == "profile-level-id" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "config" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( attrName == "mpeg4-iod" )
|
||||
{
|
||||
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
||||
}
|
||||
else if ( attrName == "mpeg4-esid" )
|
||||
{
|
||||
// a=mpeg4-esid:201
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
else if ( attr3Tokens[0] == "config" )
|
||||
{
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||
{
|
||||
size_t t = attr2Tokens[i].find("=");
|
||||
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||
Debug(4, "sprop-parameter-sets value %s", c);
|
||||
currMedia->setSprops(std::string(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
||||
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm' :
|
||||
{
|
||||
StringVector tokens = split( line, " " );
|
||||
if ( tokens.size() < 4 )
|
||||
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
||||
std::string mediaType = tokens[0];
|
||||
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
||||
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
||||
StringVector portTokens = split( tokens[1], "/" );
|
||||
int mediaPort = atoi(portTokens[0].c_str());
|
||||
int mediaNumPorts = 1;
|
||||
if ( portTokens.size() > 1 )
|
||||
mediaNumPorts = atoi(portTokens[1].c_str());
|
||||
std::string mediaTransport = tokens[2];
|
||||
if ( mediaTransport != "RTP/AVP" )
|
||||
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
||||
int payloadType = atoi(tokens[3].c_str());
|
||||
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
||||
mMediaList.push_back( currMedia );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( attrName == "mpeg4-iod" )
|
||||
{
|
||||
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
||||
}
|
||||
else if ( attrName == "mpeg4-esid" )
|
||||
{
|
||||
// a=mpeg4-esid:201
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm' :
|
||||
{
|
||||
StringVector tokens = split( line, " " );
|
||||
if ( tokens.size() < 4 )
|
||||
throw Exception( "Can't parse SDP media description '"+line+"'" );
|
||||
std::string mediaType = tokens[0];
|
||||
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
||||
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" );
|
||||
StringVector portTokens = split( tokens[1], "/" );
|
||||
int mediaPort = atoi(portTokens[0].c_str());
|
||||
int mediaNumPorts = 1;
|
||||
if ( portTokens.size() > 1 )
|
||||
mediaNumPorts = atoi(portTokens[1].c_str());
|
||||
std::string mediaTransport = tokens[2];
|
||||
if ( mediaTransport != "RTP/AVP" )
|
||||
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" );
|
||||
int payloadType = atoi(tokens[3].c_str());
|
||||
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType );
|
||||
mMediaList.push_back( currMedia );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SessionDescriptor::~SessionDescriptor()
|
||||
{
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
delete mBandInfo;
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
delete mMediaList[i];
|
||||
}
|
||||
|
||||
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
||||
{
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
|
||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||
/*
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
if ( mInfo.length() )
|
||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||
*/
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
//formatContext->nb_streams = mMediaList.size();
|
||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||
{
|
||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
AVStream *stream = av_new_stream( formatContext, i );
|
||||
#else
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||
stream->id = i;
|
||||
#endif
|
||||
|
||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||
#else
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
std::string codec_name;
|
||||
std::string codec_name;
|
||||
#endif
|
||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||
{
|
||||
// Look in static table
|
||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||
{
|
||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||
{
|
||||
// Look in static table
|
||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||
{
|
||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||
{
|
||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
#endif
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||
break;
|
||||
}
|
||||
else
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look in dynamic table
|
||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||
{
|
||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||
{
|
||||
// Look in dynamic table
|
||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||
{
|
||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||
{
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||
#else
|
||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||
#endif
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
if ( codec_name.empty() )
|
||||
#else
|
||||
if ( !stream->codec->codec_name[0] )
|
||||
#endif
|
||||
{
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
//return( 0 );
|
||||
}
|
||||
if ( mediaDesc->getWidth() )
|
||||
stream->codec->width = mediaDesc->getWidth();
|
||||
if ( mediaDesc->getHeight() )
|
||||
stream->codec->height = mediaDesc->getHeight();
|
||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||
{
|
||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||
stream->codec->extradata_size= 0;
|
||||
stream->codec->extradata= NULL;
|
||||
char pvalue[1024], *value = pvalue;
|
||||
|
||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||
|
||||
while (*value) {
|
||||
char base64packet[1024];
|
||||
uint8_t decoded_packet[1024];
|
||||
uint32_t packet_size;
|
||||
char *dst = base64packet;
|
||||
|
||||
while (*value && *value != ','
|
||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||
*dst++ = *value++;
|
||||
}
|
||||
*dst++ = '\0';
|
||||
|
||||
if (*value == ',')
|
||||
value++;
|
||||
|
||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||
if (packet_size) {
|
||||
uint8_t *dest =
|
||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||
stream->codec->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if(dest) {
|
||||
if(stream->codec->extradata_size) {
|
||||
// av_realloc?
|
||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||
av_free(stream->codec->extradata);
|
||||
}
|
||||
|
||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
stream->codec->extradata= dest;
|
||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||
// } else {
|
||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||
// return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||
stream->codec->sample_rate = mediaDesc->getClock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( formatContext );
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
if ( codec_name.empty() )
|
||||
#else
|
||||
if ( !stream->codec->codec_name[0] )
|
||||
#endif
|
||||
{
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
//return( 0 );
|
||||
}
|
||||
if ( mediaDesc->getWidth() )
|
||||
stream->codec->width = mediaDesc->getWidth();
|
||||
if ( mediaDesc->getHeight() )
|
||||
stream->codec->height = mediaDesc->getHeight();
|
||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||
{
|
||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||
stream->codec->extradata_size= 0;
|
||||
stream->codec->extradata= NULL;
|
||||
char pvalue[1024], *value = pvalue;
|
||||
|
||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||
|
||||
while (*value) {
|
||||
char base64packet[1024];
|
||||
uint8_t decoded_packet[1024];
|
||||
uint32_t packet_size;
|
||||
char *dst = base64packet;
|
||||
|
||||
while (*value && *value != ','
|
||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||
*dst++ = *value++;
|
||||
}
|
||||
*dst++ = '\0';
|
||||
|
||||
if (*value == ',')
|
||||
value++;
|
||||
|
||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||
if (packet_size) {
|
||||
uint8_t *dest =
|
||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||
stream->codec->extradata_size +
|
||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if(dest) {
|
||||
if(stream->codec->extradata_size) {
|
||||
// av_realloc?
|
||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||
av_free(stream->codec->extradata);
|
||||
}
|
||||
|
||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
stream->codec->extradata= dest;
|
||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||
// } else {
|
||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||
// return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( formatContext );
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
348
src/zm_sdp.h
348
src/zm_sdp.h
|
@ -34,204 +34,204 @@
|
|||
class SessionDescriptor
|
||||
{
|
||||
protected:
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
struct StaticPayloadDesc
|
||||
{
|
||||
int payloadType;
|
||||
const char payloadName[6];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
_AVCODECID codecId;
|
||||
int clockRate;
|
||||
int autoChannels;
|
||||
};
|
||||
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
struct DynamicPayloadDesc
|
||||
{
|
||||
const char payloadName[32];
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
AVMediaType codecType;
|
||||
AVMediaType codecType;
|
||||
#else
|
||||
enum CodecType codecType;
|
||||
enum CodecType codecType;
|
||||
#endif
|
||||
_AVCODECID codecId;
|
||||
_AVCODECID codecId;
|
||||
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
//int clockRate;
|
||||
//int autoChannels;
|
||||
};
|
||||
|
||||
public:
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
class ConnInfo
|
||||
{
|
||||
protected:
|
||||
std::string mNetworkType;
|
||||
std::string mAddressType;
|
||||
std::string mAddress;
|
||||
int mTtl;
|
||||
int mNoAddresses;
|
||||
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
public:
|
||||
ConnInfo( const std::string &connInfo );
|
||||
};
|
||||
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
class BandInfo
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mValue;
|
||||
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
public:
|
||||
BandInfo( const std::string &bandInfo );
|
||||
};
|
||||
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
class MediaDescriptor
|
||||
{
|
||||
protected:
|
||||
std::string mType;
|
||||
int mPort;
|
||||
int mNumPorts;
|
||||
std::string mTransport;
|
||||
int mPayloadType;
|
||||
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
std::string mSprops;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
|
||||
public:
|
||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||
|
||||
const std::string &getType() const
|
||||
{
|
||||
return( mType );
|
||||
}
|
||||
int getPort() const
|
||||
{
|
||||
return( mPort );
|
||||
}
|
||||
int getNumPorts() const
|
||||
{
|
||||
return( mNumPorts );
|
||||
}
|
||||
const std::string &getTransport() const
|
||||
{
|
||||
return( mTransport );
|
||||
}
|
||||
const int getPayloadType() const
|
||||
{
|
||||
return( mPayloadType );
|
||||
}
|
||||
|
||||
const std::string &getPayloadDesc() const
|
||||
{
|
||||
return( mPayloadDesc );
|
||||
}
|
||||
void setPayloadDesc( const std::string &payloadDesc )
|
||||
{
|
||||
mPayloadDesc = payloadDesc;
|
||||
}
|
||||
|
||||
const std::string &getControlUrl() const
|
||||
{
|
||||
return( mControlUrl );
|
||||
}
|
||||
void setControlUrl( const std::string &controlUrl )
|
||||
{
|
||||
mControlUrl = controlUrl;
|
||||
}
|
||||
|
||||
const int getClock() const
|
||||
{
|
||||
return( mClock );
|
||||
}
|
||||
void setClock( int clock )
|
||||
{
|
||||
mClock = clock;
|
||||
}
|
||||
|
||||
void setFrameSize( int width, int height )
|
||||
{
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
int getWidth() const
|
||||
{
|
||||
return( mWidth );
|
||||
}
|
||||
int getHeight() const
|
||||
{
|
||||
return( mHeight );
|
||||
}
|
||||
|
||||
void setSprops(const std::string props)
|
||||
{
|
||||
mSprops = props;
|
||||
}
|
||||
const std::string getSprops() const
|
||||
{
|
||||
return ( mSprops );
|
||||
}
|
||||
const double getFrameRate() const
|
||||
{
|
||||
return( mFrameRate );
|
||||
}
|
||||
void setFrameRate( double frameRate )
|
||||
{
|
||||
mFrameRate = frameRate;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<MediaDescriptor *> MediaList;
|
||||
|
||||
protected:
|
||||
static StaticPayloadDesc smStaticPayloads[];
|
||||
static DynamicPayloadDesc smDynamicPayloads[];
|
||||
|
||||
protected:
|
||||
std::string mUrl;
|
||||
|
||||
std::string mVersion;
|
||||
std::string mOwner;
|
||||
std::string mName;
|
||||
std::string mInfo;
|
||||
std::string mPayloadDesc;
|
||||
std::string mControlUrl;
|
||||
double mFrameRate;
|
||||
int mClock;
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
std::string mSprops;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
BandInfo *mBandInfo;
|
||||
std::string mTimeInfo;
|
||||
StringVector mAttributes;
|
||||
|
||||
MediaList mMediaList;
|
||||
public:
|
||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||
|
||||
const std::string &getType() const
|
||||
{
|
||||
return( mType );
|
||||
}
|
||||
int getPort() const
|
||||
{
|
||||
return( mPort );
|
||||
}
|
||||
int getNumPorts() const
|
||||
{
|
||||
return( mNumPorts );
|
||||
}
|
||||
const std::string &getTransport() const
|
||||
{
|
||||
return( mTransport );
|
||||
}
|
||||
const int getPayloadType() const
|
||||
{
|
||||
return( mPayloadType );
|
||||
}
|
||||
|
||||
const std::string &getPayloadDesc() const
|
||||
{
|
||||
return( mPayloadDesc );
|
||||
}
|
||||
void setPayloadDesc( const std::string &payloadDesc )
|
||||
{
|
||||
mPayloadDesc = payloadDesc;
|
||||
}
|
||||
|
||||
const std::string &getControlUrl() const
|
||||
{
|
||||
return( mControlUrl );
|
||||
}
|
||||
void setControlUrl( const std::string &controlUrl )
|
||||
{
|
||||
mControlUrl = controlUrl;
|
||||
}
|
||||
|
||||
const int getClock() const
|
||||
{
|
||||
return( mClock );
|
||||
}
|
||||
void setClock( int clock )
|
||||
{
|
||||
mClock = clock;
|
||||
}
|
||||
|
||||
void setFrameSize( int width, int height )
|
||||
{
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
int getWidth() const
|
||||
{
|
||||
return( mWidth );
|
||||
}
|
||||
int getHeight() const
|
||||
{
|
||||
return( mHeight );
|
||||
}
|
||||
|
||||
void setSprops(const std::string props)
|
||||
{
|
||||
mSprops = props;
|
||||
}
|
||||
const std::string getSprops() const
|
||||
{
|
||||
return ( mSprops );
|
||||
}
|
||||
const double getFrameRate() const
|
||||
{
|
||||
return( mFrameRate );
|
||||
}
|
||||
void setFrameRate( double frameRate )
|
||||
{
|
||||
mFrameRate = frameRate;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<MediaDescriptor *> MediaList;
|
||||
|
||||
protected:
|
||||
static StaticPayloadDesc smStaticPayloads[];
|
||||
static DynamicPayloadDesc smDynamicPayloads[];
|
||||
|
||||
protected:
|
||||
std::string mUrl;
|
||||
|
||||
std::string mVersion;
|
||||
std::string mOwner;
|
||||
std::string mName;
|
||||
std::string mInfo;
|
||||
|
||||
ConnInfo *mConnInfo;
|
||||
BandInfo *mBandInfo;
|
||||
std::string mTimeInfo;
|
||||
StringVector mAttributes;
|
||||
|
||||
MediaList mMediaList;
|
||||
|
||||
public:
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||
~SessionDescriptor();
|
||||
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
const std::string &getUrl() const
|
||||
{
|
||||
return( mUrl );
|
||||
}
|
||||
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
int getNumStreams() const
|
||||
{
|
||||
return( mMediaList.size() );
|
||||
}
|
||||
MediaDescriptor *getStream( int index )
|
||||
{
|
||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||
return( 0 );
|
||||
return( mMediaList[index] );
|
||||
}
|
||||
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
AVFormatContext *generateFormatContext() const;
|
||||
};
|
||||
#if 0
|
||||
v=0
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||
#include <sys/sendfile.h>
|
||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
||||
int err;
|
||||
int err;
|
||||
|
||||
err = sendfile(out_fd, in_fd, offset, size);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
err = sendfile(out_fd, in_fd, offset, size);
|
||||
if (err < 0)
|
||||
return -errno;
|
||||
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
#elif HAVE_SENDFILE7_SUPPORT
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
int err;
|
||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||
if (err && errno != EAGAIN)
|
||||
return -errno;
|
||||
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
if (size) {
|
||||
*offset += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
return -EAGAIN;
|
||||
return -EAGAIN;
|
||||
}
|
||||
#else
|
||||
#error "Your platform does not support sendfile. Sorry."
|
||||
|
|
|
@ -32,326 +32,326 @@
|
|||
StreamBase::~StreamBase()
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
#endif
|
||||
closeComms();
|
||||
closeComms();
|
||||
}
|
||||
|
||||
bool StreamBase::loadMonitor( int monitor_id )
|
||||
{
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||
{
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return( false );
|
||||
}
|
||||
monitor->connect();
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool StreamBase::checkInitialised()
|
||||
{
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
if ( !monitor )
|
||||
{
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
|
||||
void StreamBase::updateFrameRate( double fps )
|
||||
{
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
base_fps = fps;
|
||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||
frame_mod = 1;
|
||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||
// Min frame repeat?
|
||||
while( effective_fps > maxfps )
|
||||
{
|
||||
effective_fps /= 2.0;
|
||||
frame_mod *= 2;
|
||||
}
|
||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||
}
|
||||
|
||||
bool StreamBase::checkCommandQueue()
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
CmdMsg msg;
|
||||
memset( &msg, 0, sizeof(msg) );
|
||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||
if ( nbytes < 0 )
|
||||
{
|
||||
CmdMsg msg;
|
||||
memset( &msg, 0, sizeof(msg) );
|
||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||
if ( nbytes < 0 )
|
||||
{
|
||||
if ( errno != EAGAIN )
|
||||
{
|
||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||
}
|
||||
}
|
||||
//else if ( (nbytes != sizeof(msg)) )
|
||||
//{
|
||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||
//}
|
||||
else
|
||||
{
|
||||
processCommand( &msg );
|
||||
return( true );
|
||||
}
|
||||
if ( errno != EAGAIN )
|
||||
{
|
||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
//else if ( (nbytes != sizeof(msg)) )
|
||||
//{
|
||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||
//}
|
||||
else
|
||||
{
|
||||
processCommand( &msg );
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
Image *StreamBase::prepareImage( Image *image )
|
||||
{
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
static int last_scale = 0;
|
||||
static int last_zoom = 0;
|
||||
static int last_x = 0;
|
||||
static int last_y = 0;
|
||||
|
||||
if ( !last_scale )
|
||||
last_scale = scale;
|
||||
if ( !last_zoom )
|
||||
last_zoom = zoom;
|
||||
|
||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||
bool optimisedScaling = false;
|
||||
|
||||
bool image_copied = false;
|
||||
|
||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||
|
||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||
|
||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||
|
||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||
|
||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||
|
||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||
|
||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||
|
||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||
|
||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||
|
||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||
|
||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||
|
||||
if ( mag != ZM_SCALE_BASE )
|
||||
{
|
||||
if ( act_mag != ZM_SCALE_BASE )
|
||||
{
|
||||
Debug( 3, "Magnifying by %d", mag );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Scale( mag );
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
||||
|
||||
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
||||
{
|
||||
static Box last_crop;
|
||||
|
||||
if ( mag != last_mag || x != last_x || y != last_y )
|
||||
{
|
||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
||||
|
||||
//if ( !last_mag )
|
||||
//last_mag = mag;
|
||||
|
||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||
last_crop = Box();
|
||||
|
||||
Debug( 3, "Recalculating crop" );
|
||||
// Recalculate crop parameters, as %ges
|
||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||
click_x += ( x * 100 ) / last_virt_image_width;
|
||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||
click_y += ( y * 100 ) / last_virt_image_height;
|
||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
||||
|
||||
// Convert the click locations to the current image pixels
|
||||
click_x = ( click_x * act_image_width ) / 100;
|
||||
click_y = ( click_y * act_image_height ) / 100;
|
||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
||||
|
||||
int lo_x = click_x - (send_image_width/2);
|
||||
if ( lo_x < 0 )
|
||||
lo_x = 0;
|
||||
int hi_x = lo_x + (send_image_width-1);
|
||||
if ( hi_x >= act_image_width )
|
||||
{
|
||||
hi_x = act_image_width - 1;
|
||||
lo_x = hi_x - (send_image_width - 1);
|
||||
}
|
||||
|
||||
int lo_y = click_y - (send_image_height/2);
|
||||
if ( lo_y < 0 )
|
||||
lo_y = 0;
|
||||
int hi_y = lo_y + (send_image_height-1);
|
||||
if ( hi_y >= act_image_height )
|
||||
{
|
||||
hi_y = act_image_height - 1;
|
||||
lo_y = hi_y - (send_image_height - 1);
|
||||
}
|
||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||
}
|
||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Crop( last_crop );
|
||||
}
|
||||
if ( !last_scale )
|
||||
last_scale = scale;
|
||||
if ( !last_zoom )
|
||||
last_zoom = zoom;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
return( image );
|
||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||
bool optimisedScaling = false;
|
||||
|
||||
bool image_copied = false;
|
||||
|
||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||
|
||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||
|
||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||
|
||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||
|
||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||
|
||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||
|
||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||
|
||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||
|
||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||
|
||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||
|
||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||
|
||||
if ( mag != ZM_SCALE_BASE )
|
||||
{
|
||||
if ( act_mag != ZM_SCALE_BASE )
|
||||
{
|
||||
Debug( 3, "Magnifying by %d", mag );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Scale( mag );
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
||||
|
||||
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
||||
{
|
||||
static Box last_crop;
|
||||
|
||||
if ( mag != last_mag || x != last_x || y != last_y )
|
||||
{
|
||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
||||
|
||||
//if ( !last_mag )
|
||||
//last_mag = mag;
|
||||
|
||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||
last_crop = Box();
|
||||
|
||||
Debug( 3, "Recalculating crop" );
|
||||
// Recalculate crop parameters, as %ges
|
||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||
click_x += ( x * 100 ) / last_virt_image_width;
|
||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||
click_y += ( y * 100 ) / last_virt_image_height;
|
||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
||||
|
||||
// Convert the click locations to the current image pixels
|
||||
click_x = ( click_x * act_image_width ) / 100;
|
||||
click_y = ( click_y * act_image_height ) / 100;
|
||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
||||
|
||||
int lo_x = click_x - (send_image_width/2);
|
||||
if ( lo_x < 0 )
|
||||
lo_x = 0;
|
||||
int hi_x = lo_x + (send_image_width-1);
|
||||
if ( hi_x >= act_image_width )
|
||||
{
|
||||
hi_x = act_image_width - 1;
|
||||
lo_x = hi_x - (send_image_width - 1);
|
||||
}
|
||||
|
||||
int lo_y = click_y - (send_image_height/2);
|
||||
if ( lo_y < 0 )
|
||||
lo_y = 0;
|
||||
int hi_y = lo_y + (send_image_height-1);
|
||||
if ( hi_y >= act_image_height )
|
||||
{
|
||||
hi_y = act_image_height - 1;
|
||||
lo_y = hi_y - (send_image_height - 1);
|
||||
}
|
||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||
}
|
||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
||||
if ( !image_copied )
|
||||
{
|
||||
static Image copy_image;
|
||||
copy_image.Assign( *image );
|
||||
image = ©_image;
|
||||
image_copied = true;
|
||||
}
|
||||
image->Crop( last_crop );
|
||||
}
|
||||
last_scale = scale;
|
||||
last_zoom = zoom;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
return( image );
|
||||
}
|
||||
|
||||
bool StreamBase::sendTextFrame( const char *frame_text )
|
||||
{
|
||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||
|
||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
if ( scale != 100 )
|
||||
{
|
||||
image.Scale( scale );
|
||||
}
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( type == STREAM_MPEG )
|
||||
if ( type == STREAM_MPEG )
|
||||
{
|
||||
if ( !vid_stream )
|
||||
{
|
||||
if ( !vid_stream )
|
||||
{
|
||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||
vid_stream->OpenStream();
|
||||
}
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||
vid_stream->OpenStream();
|
||||
}
|
||||
else
|
||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||
}
|
||||
else
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
{
|
||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||
int n_bytes = 0;
|
||||
|
||||
image.EncodeJpeg( buffer, &n_bytes );
|
||||
|
||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||
{
|
||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||
int n_bytes = 0;
|
||||
|
||||
image.EncodeJpeg( buffer, &n_bytes );
|
||||
|
||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||
{
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||
return( false );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
fprintf( stdout, "\r\n\r\n" );
|
||||
fflush( stdout );
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT( now );
|
||||
return( true );
|
||||
}
|
||||
|
||||
void StreamBase::openComms()
|
||||
{
|
||||
if ( connkey > 0 )
|
||||
if ( connkey > 0 )
|
||||
{
|
||||
|
||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||
|
||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if ( lock_fd <= 0 )
|
||||
{
|
||||
|
||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||
|
||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if ( lock_fd <= 0 )
|
||||
{
|
||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
lock_fd = 0;
|
||||
}
|
||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||
{
|
||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
|
||||
close(lock_fd);
|
||||
lock_fd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||
}
|
||||
|
||||
|
||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||
unlink( loc_sock_path );
|
||||
|
||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||
loc_addr.sun_family = AF_UNIX;
|
||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||
{
|
||||
Fatal( "Can't bind: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||
rem_addr.sun_family = AF_UNIX;
|
||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
lock_fd = 0;
|
||||
}
|
||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||
{
|
||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||
|
||||
close(lock_fd);
|
||||
lock_fd = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||
}
|
||||
|
||||
|
||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||
unlink( loc_sock_path );
|
||||
|
||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||
loc_addr.sun_family = AF_UNIX;
|
||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||
{
|
||||
Fatal( "Can't bind: %s", strerror(errno) );
|
||||
}
|
||||
|
||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||
rem_addr.sun_family = AF_UNIX;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamBase::closeComms()
|
||||
{
|
||||
if ( connkey > 0 )
|
||||
if ( connkey > 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
if ( sd >= 0 )
|
||||
{
|
||||
close( sd );
|
||||
sd = -1;
|
||||
}
|
||||
if ( loc_sock_path[0] )
|
||||
{
|
||||
unlink( loc_sock_path );
|
||||
}
|
||||
if (lock_fd > 0)
|
||||
{
|
||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||
unlink(sock_path_lock);
|
||||
}
|
||||
close( sd );
|
||||
sd = -1;
|
||||
}
|
||||
if ( loc_sock_path[0] )
|
||||
{
|
||||
unlink( loc_sock_path );
|
||||
}
|
||||
if (lock_fd > 0)
|
||||
{
|
||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||
unlink(sock_path_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
220
src/zm_stream.h
220
src/zm_stream.h
|
@ -33,149 +33,149 @@ class Monitor;
|
|||
class StreamBase
|
||||
{
|
||||
public:
|
||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||
|
||||
protected:
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||
enum { DEFAULT_MAXFPS=10 };
|
||||
enum { DEFAULT_BITRATE=100000 };
|
||||
|
||||
protected:
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[16];
|
||||
} CmdMsg;
|
||||
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
typedef struct {
|
||||
int msg_type;
|
||||
char msg_data[256];
|
||||
} DataMsg;
|
||||
|
||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||
|
||||
protected:
|
||||
Monitor *monitor;
|
||||
Monitor *monitor;
|
||||
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
StreamType type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
int zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short x, y;
|
||||
|
||||
protected:
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
int connkey;
|
||||
int sd;
|
||||
char loc_sock_path[PATH_MAX];
|
||||
struct sockaddr_un loc_addr;
|
||||
char rem_sock_path[PATH_MAX];
|
||||
struct sockaddr_un rem_addr;
|
||||
char sock_path_lock[PATH_MAX];
|
||||
int lock_fd;
|
||||
|
||||
protected:
|
||||
bool paused;
|
||||
int step;
|
||||
bool paused;
|
||||
int step;
|
||||
|
||||
struct timeval now;
|
||||
struct timeval now;
|
||||
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
VideoStream *vid_stream;
|
||||
VideoStream *vid_stream;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
||||
CmdMsg msg;
|
||||
CmdMsg msg;
|
||||
|
||||
protected:
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
bool loadMonitor( int monitor_id );
|
||||
bool checkInitialised();
|
||||
void updateFrameRate( double fps );
|
||||
Image *prepareImage( Image *image );
|
||||
bool sendTextFrame( const char *text );
|
||||
bool checkCommandQueue();
|
||||
virtual void processCommand( const CmdMsg *msg )=0;
|
||||
|
||||
public:
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
StreamBase()
|
||||
{
|
||||
monitor = 0;
|
||||
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
type = DEFAULT_TYPE;
|
||||
format = "";
|
||||
replay_rate = DEFAULT_RATE;
|
||||
scale = DEFAULT_SCALE;
|
||||
zoom = DEFAULT_ZOOM;
|
||||
maxfps = DEFAULT_MAXFPS;
|
||||
bitrate = DEFAULT_BITRATE;
|
||||
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
paused = false;
|
||||
step = 0;
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
connkey = 0;
|
||||
sd = -1;
|
||||
lock_fd = 0;
|
||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
vid_stream = 0;
|
||||
vid_stream = 0;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
void setStreamType( StreamType p_type )
|
||||
{
|
||||
type = p_type;
|
||||
}
|
||||
void setStreamFormat( const char *p_format )
|
||||
{
|
||||
format = p_format;
|
||||
}
|
||||
void setStreamScale( int p_scale )
|
||||
{
|
||||
scale = p_scale;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate )
|
||||
{
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps )
|
||||
{
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate( int p_bitrate )
|
||||
{
|
||||
bitrate = p_bitrate;
|
||||
}
|
||||
void setStreamQueue( int p_connkey )
|
||||
{
|
||||
connkey = p_connkey;
|
||||
}
|
||||
virtual void openComms();
|
||||
virtual void closeComms();
|
||||
virtual void runStream()=0;
|
||||
};
|
||||
|
||||
#endif // ZM_STREAM_H
|
||||
|
|
|
@ -29,311 +29,311 @@
|
|||
|
||||
struct timespec getTimeout( int secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
struct timespec getTimeout( double secs )
|
||||
{
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
struct timespec timeout;
|
||||
struct timeval temp_timeout;
|
||||
gettimeofday( &temp_timeout, 0 );
|
||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||
if ( timeout.tv_nsec > 1000000000 )
|
||||
{
|
||||
timeout.tv_sec += 1;
|
||||
timeout.tv_nsec -= 1000000000;
|
||||
}
|
||||
return( timeout );
|
||||
}
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
if ( locked() )
|
||||
Warning( "Destroying mutex when locked" );
|
||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock()
|
||||
{
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( int secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::lock( double secs )
|
||||
{
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Mutex::unlock()
|
||||
{
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Mutex::locked()
|
||||
{
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
int state = pthread_mutex_trylock( &mMutex );
|
||||
if ( state != 0 && state != EBUSY )
|
||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||
if ( state != EBUSY )
|
||||
unlock();
|
||||
return( state == EBUSY );
|
||||
}
|
||||
|
||||
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::wait()
|
||||
{
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
// Locking done outside of this function
|
||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
bool Condition::wait( int secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
Debug( 8, "Waiting for %d seconds", secs );
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
bool Condition::wait( double secs )
|
||||
{
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
// Locking done outside of this function
|
||||
struct timespec timeout = getTimeout( secs );
|
||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||
return( errno != ETIMEDOUT );
|
||||
}
|
||||
|
||||
void Condition::signal()
|
||||
{
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
void Condition::broadcast()
|
||||
{
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getValue() const
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> T ThreadData<T>::setValue( const T value )
|
||||
{
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
mMutex.lock();
|
||||
const T valueCopy = mValue = value;
|
||||
mMutex.unlock();
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %p", this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait();
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
||||
{
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||
mMutex.lock();
|
||||
mChanged = false;
|
||||
//do {
|
||||
mCondition.wait( secs );
|
||||
//} while ( !mChanged );
|
||||
const T valueCopy = mValue;
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Got value update, %p", this );
|
||||
return( valueCopy );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with signal, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.signal();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
||||
{
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
Debug( 8, "Updating value with broadcast, %p", this );
|
||||
mMutex.lock();
|
||||
mValue = value;
|
||||
mChanged = true;
|
||||
mCondition.broadcast();
|
||||
mMutex.unlock();
|
||||
Debug( 9, "Updated value, %p", this );
|
||||
}
|
||||
|
||||
Thread::Thread() :
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
mThreadCondition( mThreadMutex ),
|
||||
mPid( -1 ),
|
||||
mStarted( false ),
|
||||
mRunning( false )
|
||||
{
|
||||
Debug( 1, "Creating thread" );
|
||||
Debug( 1, "Creating thread" );
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
Debug( 1, "Destroying thread %d", mPid );
|
||||
if ( mStarted )
|
||||
join();
|
||||
}
|
||||
|
||||
void *Thread::mThreadFunc( void *arg )
|
||||
{
|
||||
Debug( 2, "Invoking thread" );
|
||||
Debug( 2, "Invoking thread" );
|
||||
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
void *status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
int run=(thisPtr->run());
|
||||
status = (void *)&run;
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", status );
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
status = (void *)-1;
|
||||
Debug( 2, "Exiting thread after exception, status %p", status );
|
||||
}
|
||||
return( status );
|
||||
Thread *thisPtr = (Thread *)arg;
|
||||
void *status = 0;
|
||||
try
|
||||
{
|
||||
thisPtr->mThreadMutex.lock();
|
||||
thisPtr->mPid = thisPtr->id();
|
||||
thisPtr->mThreadCondition.signal();
|
||||
thisPtr->mThreadMutex.unlock();
|
||||
thisPtr->mRunning = true;
|
||||
int run=(thisPtr->run());
|
||||
status = (void *)&run;
|
||||
thisPtr->mRunning = false;
|
||||
Debug( 2, "Exiting thread, status %p", status );
|
||||
}
|
||||
catch ( const ThreadException &e )
|
||||
{
|
||||
Error( "%s", e.getMessage().c_str() );
|
||||
thisPtr->mRunning = false;
|
||||
status = (void *)-1;
|
||||
Debug( 2, "Exiting thread after exception, status %p", status );
|
||||
}
|
||||
return( status );
|
||||
}
|
||||
|
||||
void Thread::start()
|
||||
{
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
Debug( 1, "Starting thread" );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self start thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( !mStarted )
|
||||
{
|
||||
pthread_attr_t threadAttrs;
|
||||
pthread_attr_init( &threadAttrs );
|
||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
mStarted = true;
|
||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||
pthread_attr_destroy( &threadAttrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempt to start already running thread %d", mPid );
|
||||
}
|
||||
mThreadCondition.wait();
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Started thread %d", mPid );
|
||||
}
|
||||
|
||||
void Thread::join()
|
||||
{
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
Debug( 1, "Joining thread %d", mPid );
|
||||
if ( isThread() )
|
||||
throw ThreadException( "Can't self join thread" );
|
||||
mThreadMutex.lock();
|
||||
if ( mPid >= 0 )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
if ( mStarted )
|
||||
{
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join already finished thread %d", mPid );
|
||||
}
|
||||
void *threadStatus = 0;
|
||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||
mStarted = false;
|
||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join non-started thread %d", mPid );
|
||||
Warning( "Attempt to join already finished thread %d", mPid );
|
||||
}
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Joined thread %d", mPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Attempt to join non-started thread %d", mPid );
|
||||
}
|
||||
mThreadMutex.unlock();
|
||||
Debug( 1, "Joined thread %d", mPid );
|
||||
}
|
||||
|
||||
void Thread::kill( int signal )
|
||||
{
|
||||
pthread_kill( mThread, signal );
|
||||
pthread_kill( mThread, signal );
|
||||
}
|
||||
|
||||
// Some explicit template instantiations
|
||||
|
|
306
src/zm_thread.h
306
src/zm_thread.h
|
@ -36,27 +36,27 @@ class ThreadException : public Exception
|
|||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
# else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
public:
|
||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||
}
|
||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||
}
|
||||
};
|
||||
|
||||
class Mutex
|
||||
|
@ -64,214 +64,214 @@ class Mutex
|
|||
friend class Condition;
|
||||
|
||||
private:
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_mutex_t mMutex;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
Mutex();
|
||||
~Mutex();
|
||||
|
||||
private:
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
pthread_mutex_t *getMutex()
|
||||
{
|
||||
return( &mMutex );
|
||||
}
|
||||
|
||||
public:
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
void lock();
|
||||
void lock( int secs );
|
||||
void lock( double secs );
|
||||
void unlock();
|
||||
bool locked();
|
||||
};
|
||||
|
||||
class ScopedMutex
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
Mutex &mMutex;
|
||||
|
||||
public:
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||
{
|
||||
mMutex.lock();
|
||||
}
|
||||
~ScopedMutex()
|
||||
{
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
ScopedMutex( const ScopedMutex & );
|
||||
};
|
||||
|
||||
class Condition
|
||||
{
|
||||
private:
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
Mutex &mMutex;
|
||||
pthread_cond_t mCondition;
|
||||
|
||||
public:
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
Condition( Mutex &mutex );
|
||||
~Condition();
|
||||
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
void wait();
|
||||
bool wait( int secs );
|
||||
bool wait( double secs );
|
||||
void signal();
|
||||
void broadcast();
|
||||
};
|
||||
|
||||
class Semaphore : public Condition
|
||||
{
|
||||
private:
|
||||
Mutex mMutex;
|
||||
Mutex mMutex;
|
||||
|
||||
public:
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
Semaphore() : Condition( mMutex )
|
||||
{
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void wait()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::wait();
|
||||
mMutex.unlock();
|
||||
}
|
||||
bool wait( int secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
bool wait( double secs )
|
||||
{
|
||||
mMutex.lock();
|
||||
bool result = Condition::wait( secs );
|
||||
mMutex.unlock();
|
||||
return( result );
|
||||
}
|
||||
void signal()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::signal();
|
||||
mMutex.unlock();
|
||||
}
|
||||
void broadcast()
|
||||
{
|
||||
mMutex.lock();
|
||||
Condition::broadcast();
|
||||
mMutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class ThreadData
|
||||
{
|
||||
private:
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
T mValue;
|
||||
mutable bool mChanged;
|
||||
mutable Mutex mMutex;
|
||||
mutable Condition mCondition;
|
||||
|
||||
public:
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||
{
|
||||
}
|
||||
//~ThreadData() {}
|
||||
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
__attribute__((used)) operator T() const
|
||||
{
|
||||
return( getValue() );
|
||||
}
|
||||
__attribute__((used)) const T operator=( const T value )
|
||||
{
|
||||
return( setValue( value ) );
|
||||
}
|
||||
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
__attribute__((used)) const T getValueImmediate() const
|
||||
{
|
||||
return( mValue );
|
||||
}
|
||||
__attribute__((used)) T setValueImmediate( const T value )
|
||||
{
|
||||
return( mValue = value );
|
||||
}
|
||||
__attribute__((used)) const T getValue() const;
|
||||
__attribute__((used)) T setValue( const T value );
|
||||
__attribute__((used)) const T getUpdatedValue() const;
|
||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||
__attribute__((used)) void updateValueSignal( const T value );
|
||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
typedef void *(*ThreadFunc)( void * );
|
||||
|
||||
protected:
|
||||
pthread_t mThread;
|
||||
pthread_t mThread;
|
||||
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
Mutex mThreadMutex;
|
||||
Condition mThreadCondition;
|
||||
#ifndef SOLARIS
|
||||
pid_t mPid;
|
||||
pid_t mPid;
|
||||
#else
|
||||
pthread_t mPid;
|
||||
pthread_t mPid;
|
||||
#endif
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
bool mStarted;
|
||||
bool mRunning;
|
||||
|
||||
protected:
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
#ifndef SOLARIS
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
pid_t id() const
|
||||
{
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
}
|
||||
#else
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
pthread_t id() const
|
||||
{
|
||||
return( pthread_self() );
|
||||
}
|
||||
#endif
|
||||
void exit( int status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
void exit( int status = 0 )
|
||||
{
|
||||
//INFO( "Exiting" );
|
||||
pthread_exit( (void *)&status );
|
||||
}
|
||||
static void *mThreadFunc( void *arg );
|
||||
|
||||
public:
|
||||
virtual int run() = 0;
|
||||
virtual int run() = 0;
|
||||
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
void start();
|
||||
void join();
|
||||
void kill( int signal );
|
||||
bool isThread()
|
||||
{
|
||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||
}
|
||||
bool isStarted() const { return( mStarted ); }
|
||||
bool isRunning() const { return( mRunning ); }
|
||||
};
|
||||
|
||||
#endif // ZM_THREAD_H
|
||||
|
|
188
src/zm_time.h
188
src/zm_time.h
|
@ -29,48 +29,48 @@
|
|||
|
||||
struct DeltaTimeval
|
||||
{
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
bool positive;
|
||||
unsigned long delta;
|
||||
unsigned long sec;
|
||||
unsigned long fsec;
|
||||
unsigned long prec;
|
||||
};
|
||||
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 10
|
||||
#define DT_PREC_1 DT_GRAN_10
|
||||
#define DT_GRAN_1000000 1000000
|
||||
#define DT_PREC_6 DT_GRAN_1000000
|
||||
#define DT_GRAN_100000 100000
|
||||
#define DT_PREC_5 DT_GRAN_100000
|
||||
#define DT_GRAN_10000 10000
|
||||
#define DT_PREC_4 DT_GRAN_10000
|
||||
#define DT_GRAN_1000 1000
|
||||
#define DT_PREC_3 DT_GRAN_1000
|
||||
#define DT_GRAN_100 100
|
||||
#define DT_PREC_2 DT_GRAN_100
|
||||
#define DT_GRAN_10 10
|
||||
#define DT_PREC_1 DT_GRAN_10
|
||||
|
||||
#define DT_MAXGRAN DT_GRAN_1000000
|
||||
#define DT_MAXGRAN DT_GRAN_1000000
|
||||
|
||||
// This obviously wouldn't work for massive deltas but as it's mostly
|
||||
// for frames it will only usually be a fraction of a second or so
|
||||
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
||||
{ \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
}
|
||||
|
||||
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
||||
{ \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||
result.positive = (delta>=0); \
|
||||
result.delta = abs(delta); \
|
||||
result.sec = result.delta/(precision); \
|
||||
result.fsec = result.delta%(precision); \
|
||||
result.prec = (precision); \
|
||||
}
|
||||
|
||||
#define USEC_PER_SEC 1000000
|
||||
|
@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t;
|
|||
|
||||
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||
}
|
||||
|
||||
inline int tvDiffUsec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffUsec( first, now ) );
|
||||
}
|
||||
|
||||
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||
}
|
||||
|
||||
inline int tvDiffMsec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffMsec( first, now ) );
|
||||
}
|
||||
|
||||
inline double tvDiffSec( struct timeval first, struct timeval last )
|
||||
{
|
||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||
}
|
||||
|
||||
inline double tvDiffSec( struct timeval first )
|
||||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
return( tvDiffSec( first, now ) );
|
||||
}
|
||||
|
||||
inline struct timeval tvZero()
|
||||
{
|
||||
struct timeval t = { 0, 0 };
|
||||
return( t );
|
||||
struct timeval t = { 0, 0 };
|
||||
return( t );
|
||||
}
|
||||
|
||||
inline int tvIsZero( const struct timeval t )
|
||||
{
|
||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||
}
|
||||
|
||||
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
if ( t1.tv_sec < t2.tv_sec )
|
||||
return( -1 );
|
||||
if ( t1.tv_sec > t2.tv_sec )
|
||||
return( 1 );
|
||||
if ( t1.tv_usec < t2.tv_usec )
|
||||
return( -1 );
|
||||
if ( t1.tv_usec > t2.tv_usec )
|
||||
return( 1 );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
inline int tvEq( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||
}
|
||||
|
||||
inline struct timeval tvNow( void )
|
||||
{
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
struct timeval t;
|
||||
gettimeofday( &t, NULL );
|
||||
return( t );
|
||||
}
|
||||
|
||||
inline struct timeval tvCheck( struct timeval &t )
|
||||
{
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||
t.tv_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
if ( t.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||
t.tv_usec %= USEC_PER_SEC;
|
||||
}
|
||||
else if ( t.tv_usec < 0 )
|
||||
{
|
||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||
t.tv_usec = 0;
|
||||
}
|
||||
return( t );
|
||||
}
|
||||
|
||||
// Add t2 to t1
|
||||
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec += t2.tv_sec;
|
||||
t1.tv_usec += t2.tv_usec;
|
||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||
{
|
||||
t1.tv_sec++;
|
||||
t1.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
return( t1 );
|
||||
}
|
||||
|
||||
// Subtract t2 from t1
|
||||
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
||||
{
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
tvCheck(t1);
|
||||
tvCheck(t2);
|
||||
t1.tv_sec -= t2.tv_sec;
|
||||
t1.tv_usec -= t2.tv_usec;
|
||||
if ( t1.tv_usec < 0 )
|
||||
{
|
||||
t1.tv_sec--;
|
||||
t1.tv_usec += USEC_PER_SEC;
|
||||
}
|
||||
return( t1 ) ;
|
||||
}
|
||||
|
||||
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
||||
{
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
struct timeval t;
|
||||
t.tv_sec = sec;
|
||||
t.tv_usec = usec;
|
||||
return( t );
|
||||
}
|
||||
|
||||
#endif // ZM_TIME_H
|
||||
|
|
114
src/zm_timer.cpp
114
src/zm_timer.cpp
|
@ -24,96 +24,96 @@
|
|||
int Timer::TimerThread::mNextTimerId = 0;
|
||||
|
||||
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
mTimerId( 0 ),
|
||||
mTimer( timer ),
|
||||
mDuration( duration ),
|
||||
mRepeat( repeat ),
|
||||
mReset( false ),
|
||||
mExpiryFlag( true )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
mTimerId = mNextTimerId++;
|
||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
Timer::TimerThread::~TimerThread()
|
||||
{
|
||||
cancel();
|
||||
cancel();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::cancel()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||
mRepeat = false;
|
||||
mReset = false;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
void Timer::TimerThread::reset()
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
mAccessMutex.lock();
|
||||
if ( mRunning )
|
||||
{
|
||||
Debug( 4, "Resetting timer" );
|
||||
mReset = true;
|
||||
mExpiryFlag.updateValueSignal( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
}
|
||||
|
||||
int Timer::TimerThread::run()
|
||||
{
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||
bool timerExpired = false;
|
||||
do
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
mAccessMutex.lock();
|
||||
mReset = false;
|
||||
mExpiryFlag.setValue( true );
|
||||
mAccessMutex.unlock();
|
||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||
mAccessMutex.lock();
|
||||
if ( timerExpired )
|
||||
{
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
} while ( mRepeat || (mReset && !timerExpired) );
|
||||
return( timerExpired );
|
||||
Debug( 4, "Timer %d expired", mTimerId );
|
||||
mTimer.expire();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||
}
|
||||
mAccessMutex.unlock();
|
||||
} while ( mRepeat || (mReset && !timerExpired) );
|
||||
return( timerExpired );
|
||||
}
|
||||
|
||||
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
||||
{
|
||||
mTimerThread.start();
|
||||
mTimerThread.start();
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
//cancel();
|
||||
//cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::cancel()
|
||||
{
|
||||
mTimerThread.cancel();
|
||||
mTimerThread.cancel();
|
||||
}
|
||||
|
||||
void Timer::Timer::reset()
|
||||
{
|
||||
mTimerThread.reset();
|
||||
mTimerThread.reset();
|
||||
}
|
||||
|
||||
|
|
120
src/zm_timer.h
120
src/zm_timer.h
|
@ -30,81 +30,81 @@
|
|||
class Timer
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
class TimerException : public Exception
|
||||
{
|
||||
private:
|
||||
#ifndef SOLARIS
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
pid_t pid() {
|
||||
pid_t tid;
|
||||
#ifdef __FreeBSD__
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
long lwpid;
|
||||
thr_self(&lwpid);
|
||||
tid = lwpid;
|
||||
#else
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#ifdef __FreeBSD_kernel__
|
||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||
#else
|
||||
tid=syscall(SYS_gettid);
|
||||
#endif
|
||||
#endif
|
||||
return tid;
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
#else
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
pthread_t pid() { return( pthread_self() ); }
|
||||
#endif
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class TimerThread : public Thread
|
||||
public:
|
||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
class TimerThread : public Thread
|
||||
{
|
||||
private:
|
||||
typedef ThreadData<bool> ExpiryFlag;
|
||||
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
private:
|
||||
static int mNextTimerId;
|
||||
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
private:
|
||||
int mTimerId;
|
||||
Timer &mTimer;
|
||||
int mDuration;
|
||||
int mRepeat;
|
||||
int mReset;
|
||||
ExpiryFlag mExpiryFlag;
|
||||
Mutex mAccessMutex;
|
||||
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
private:
|
||||
void quit()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void cancel();
|
||||
void reset();
|
||||
int run();
|
||||
};
|
||||
public:
|
||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||
~TimerThread();
|
||||
|
||||
protected:
|
||||
TimerThread mTimerThread;
|
||||
|
||||
protected:
|
||||
Timer( int timeout, bool repeat=false );
|
||||
|
||||
public:
|
||||
virtual ~Timer();
|
||||
|
||||
protected:
|
||||
virtual void expire()=0;
|
||||
|
||||
public:
|
||||
void cancel();
|
||||
void reset();
|
||||
int run();
|
||||
};
|
||||
|
||||
protected:
|
||||
TimerThread mTimerThread;
|
||||
|
||||
protected:
|
||||
Timer( int timeout, bool repeat=false );
|
||||
|
||||
public:
|
||||
virtual ~Timer();
|
||||
|
||||
protected:
|
||||
virtual void expire()=0;
|
||||
|
||||
public:
|
||||
void cancel();
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif // ZM_TIMER_H
|
||||
|
|
332
src/zm_user.cpp
332
src/zm_user.cpp
|
@ -29,119 +29,119 @@
|
|||
|
||||
User::User()
|
||||
{
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
username[0] = password[0] = 0;
|
||||
enabled = false;
|
||||
stream = events = control = monitors = system = PERM_NONE;
|
||||
monitor_ids = 0;
|
||||
}
|
||||
|
||||
User::User( MYSQL_ROW &dbrow )
|
||||
{
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
int index = 0;
|
||||
strncpy( username, dbrow[index++], sizeof(username) );
|
||||
strncpy( password, dbrow[index++], sizeof(password) );
|
||||
enabled = (bool)atoi( dbrow[index++] );
|
||||
stream = (Permission)atoi( dbrow[index++] );
|
||||
events = (Permission)atoi( dbrow[index++] );
|
||||
control = (Permission)atoi( dbrow[index++] );
|
||||
monitors = (Permission)atoi( dbrow[index++] );
|
||||
system = (Permission)atoi( dbrow[index++] );
|
||||
monitor_ids = 0;
|
||||
char *monitor_ids_str = dbrow[index++];
|
||||
if ( monitor_ids_str && *monitor_ids_str )
|
||||
{
|
||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||
int n_monitor_ids = 0;
|
||||
const char *ptr = monitor_ids_str;
|
||||
do
|
||||
{
|
||||
int id = 0;
|
||||
while( isdigit( *ptr ) )
|
||||
{
|
||||
id *= 10;
|
||||
id += *ptr-'0';
|
||||
ptr++;
|
||||
}
|
||||
if ( id )
|
||||
{
|
||||
monitor_ids[n_monitor_ids++] = id;
|
||||
if ( !*ptr )
|
||||
break;
|
||||
}
|
||||
while ( !isdigit( *ptr ) )
|
||||
ptr++;
|
||||
} while( *ptr );
|
||||
monitor_ids[n_monitor_ids] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
User::~User()
|
||||
{
|
||||
delete monitor_ids;
|
||||
delete monitor_ids;
|
||||
}
|
||||
|
||||
bool User::canAccess( int monitor_id )
|
||||
{
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
if ( !monitor_ids )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
for ( int i = 0; monitor_ids[i]; i++ )
|
||||
{
|
||||
if ( monitor_ids[i] == monitor_id )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Function to load a user from username and password
|
||||
// Please note that in auth relay mode = none, password is NULL
|
||||
User *zmLoadUser( const char *username, const char *password )
|
||||
{
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
char safer_password[129]; // current db password size is 64
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
char safer_username[65]; // current db username size is 32
|
||||
char safer_password[129]; // current db password size is 64
|
||||
|
||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||
|
||||
if ( password ) {
|
||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||
} else {
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||
}
|
||||
if ( password ) {
|
||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||
} else {
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||
}
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users != 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user %s", username );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
User *user = new User( dbrow );
|
||||
Info( "Authenticated user '%s'", user->getUsername() );
|
||||
|
||||
mysql_free_result( result );
|
||||
mysql_free_result( result );
|
||||
|
||||
return( user );
|
||||
return( user );
|
||||
}
|
||||
|
||||
// Function to validate an authentication string
|
||||
|
@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
|
|||
{
|
||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
#ifdef HAVE_GCRYPT_H
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
// Special initialisation for libgcrypt
|
||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||
{
|
||||
Fatal( "Unable to initialise libgcrypt" );
|
||||
}
|
||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||
#endif // HAVE_GCRYPT_H
|
||||
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
const char *remote_addr = "";
|
||||
if ( use_remote_addr )
|
||||
{
|
||||
remote_addr = getenv( "REMOTE_ADDR" );
|
||||
if ( !remote_addr )
|
||||
{
|
||||
Warning( "Can't determine remote address, using null" );
|
||||
remote_addr = "";
|
||||
}
|
||||
}
|
||||
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
if ( mysql_query( &dbconn, sql ) )
|
||||
{
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
if ( !result )
|
||||
{
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_users = mysql_num_rows( result );
|
||||
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
if ( n_users < 1 )
|
||||
{
|
||||
Warning( "Unable to authenticate user" );
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||
{
|
||||
const char *user = dbrow[0];
|
||||
const char *pass = dbrow[1];
|
||||
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
char auth_key[512] = "";
|
||||
char auth_md5[32+1] = "";
|
||||
size_t md5len = 16;
|
||||
unsigned char md5sum[md5len];
|
||||
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
time_t now = time( 0 );
|
||||
int max_tries = 2;
|
||||
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||
{
|
||||
struct tm *now_tm = localtime( &now );
|
||||
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||
config.auth_hash_secret,
|
||||
user,
|
||||
pass,
|
||||
remote_addr,
|
||||
now_tm->tm_hour,
|
||||
now_tm->tm_mday,
|
||||
now_tm->tm_mon,
|
||||
now_tm->tm_year
|
||||
);
|
||||
|
||||
#if HAVE_DECL_MD5
|
||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||
#endif
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
auth_md5[0] = '\0';
|
||||
for ( unsigned int j = 0; j < md5len; j++ )
|
||||
{
|
||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||
}
|
||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !strcmp( auth, auth_md5 ) )
|
||||
{
|
||||
// We have a match
|
||||
User *user = new User( dbrow );
|
||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||
return( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
#else // HAVE_DECL_MD5
|
||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||
#endif // HAVE_DECL_MD5
|
||||
return( 0 );
|
||||
return( 0 );
|
||||
}
|
||||
|
|
|
@ -39,33 +39,33 @@
|
|||
class User
|
||||
{
|
||||
public:
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||
|
||||
protected:
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
char username[32+1];
|
||||
char password[64+1];
|
||||
bool enabled;
|
||||
Permission stream;
|
||||
Permission events;
|
||||
Permission control;
|
||||
Permission monitors;
|
||||
Permission system;
|
||||
int *monitor_ids;
|
||||
|
||||
public:
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
User();
|
||||
User( MYSQL_ROW &dbrow );
|
||||
~User();
|
||||
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
const char *getUsername() const { return( username ); }
|
||||
const char *getPassword() const { return( password ); }
|
||||
bool isEnabled() const { return( enabled ); }
|
||||
Permission getStream() const { return( stream ); }
|
||||
Permission getEvents() const { return( events ); }
|
||||
Permission getControl() const { return( control ); }
|
||||
Permission getMonitors() const { return( monitors ); }
|
||||
Permission getSystem() const { return( system ); }
|
||||
bool canAccess( int monitor_id );
|
||||
};
|
||||
|
||||
User *zmLoadUser( const char *username, const char *password=0 );
|
||||
|
|
440
src/zm_utils.cpp
440
src/zm_utils.cpp
|
@ -28,256 +28,256 @@
|
|||
unsigned int sseversion = 0;
|
||||
|
||||
std::string trimSet(std::string str, std::string trimset) {
|
||||
// Trim Both leading and trailing sets
|
||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
// Trim Both leading and trailing sets
|
||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
// if all spaces or empty return an empty string
|
||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
else
|
||||
return str.substr( startpos, endpos-startpos+1 );
|
||||
}
|
||||
|
||||
std::string trimSpaces(std::string str)
|
||||
{
|
||||
return trimSet(str, " \t");
|
||||
return trimSet(str, " \t");
|
||||
}
|
||||
|
||||
std::string replaceAll(std::string str, std::string from, std::string to) {
|
||||
if(from.empty())
|
||||
return str;
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
if(from.empty())
|
||||
return str;
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const std::string stringtf( const char *format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
const std::string stringtf( const std::string &format, ... )
|
||||
{
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
va_list ap;
|
||||
char tempBuffer[8192];
|
||||
std::string tempString;
|
||||
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
va_start(ap, format );
|
||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||
va_end(ap);
|
||||
|
||||
tempString = tempBuffer;
|
||||
tempString = tempBuffer;
|
||||
|
||||
return( tempString );
|
||||
return( tempString );
|
||||
}
|
||||
|
||||
bool startsWith( const std::string &haystack, const std::string &needle )
|
||||
{
|
||||
return( haystack.substr( 0, needle.length() ) == needle );
|
||||
return( haystack.substr( 0, needle.length() ) == needle );
|
||||
}
|
||||
|
||||
StringVector split( const std::string &string, const std::string chars, int limit )
|
||||
{
|
||||
StringVector stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
StringVector stringVector;
|
||||
std::string tempString = string;
|
||||
std::string::size_type startIndex = 0;
|
||||
std::string::size_type endIndex = 0;
|
||||
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||
do
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
// Find delimiters
|
||||
endIndex = string.find_first_of( chars, startIndex );
|
||||
//Info( "Got endIndex at %d", endIndex );
|
||||
if ( endIndex > 0 )
|
||||
{
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
break;
|
||||
}
|
||||
//Info( "Got new startIndex at %d", startIndex );
|
||||
} while ( startIndex != std::string::npos );
|
||||
//Info( "Finished with %d strings", stringVector.size() );
|
||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||
}
|
||||
if ( endIndex == std::string::npos )
|
||||
break;
|
||||
// Find non-delimiters
|
||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||
{
|
||||
stringVector.push_back( string.substr( startIndex ) );
|
||||
break;
|
||||
}
|
||||
//Info( "Got new startIndex at %d", startIndex );
|
||||
} while ( startIndex != std::string::npos );
|
||||
//Info( "Finished with %d strings", stringVector.size() );
|
||||
|
||||
return( stringVector );
|
||||
return( stringVector );
|
||||
}
|
||||
|
||||
const std::string join(const StringVector v, const char * delim ) {
|
||||
std::stringstream ss;
|
||||
std::stringstream ss;
|
||||
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
for(size_t i = 0; i < v.size(); ++i) {
|
||||
if(i != 0)
|
||||
ss << ",";
|
||||
ss << v[i];
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
const std::string base64Encode( const std::string &inString )
|
||||
{
|
||||
static char base64_table[64] = { '\0' };
|
||||
static char base64_table[64] = { '\0' };
|
||||
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
if ( !base64_table[0] )
|
||||
{
|
||||
int i = 0;
|
||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
base64_table[i++] = c;
|
||||
for ( char c = '0'; c <= '9'; c++ )
|
||||
base64_table[i++] = c;
|
||||
base64_table[i++] = '+';
|
||||
base64_table[i++] = '/';
|
||||
}
|
||||
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
std::string outString;
|
||||
outString.reserve( 2 * inString.size() );
|
||||
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
const char *inPtr = inString.c_str();
|
||||
while( *inPtr )
|
||||
{
|
||||
unsigned char selection = *inPtr >> 2;
|
||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 4);
|
||||
remainder = (*inPtr++ & 0x0f) << 2;
|
||||
outString += base64_table[selection];
|
||||
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
if ( *inPtr )
|
||||
{
|
||||
selection = remainder | (*inPtr >> 6);
|
||||
outString += base64_table[selection];
|
||||
selection = (*inPtr++ & 0x3f);
|
||||
outString += base64_table[selection];
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outString += base64_table[remainder];
|
||||
outString += '=';
|
||||
outString += '=';
|
||||
}
|
||||
}
|
||||
return( outString );
|
||||
}
|
||||
|
||||
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
std::string str(string);
|
||||
size_t pos;
|
||||
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
while(true) {
|
||||
pos = str.find(delim);
|
||||
items.push_back(str.substr(0, pos));
|
||||
str.erase(0, pos+1);
|
||||
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
if(pos == std::string::npos)
|
||||
break;
|
||||
}
|
||||
|
||||
return items.size();
|
||||
return items.size();
|
||||
}
|
||||
|
||||
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
if(string == NULL)
|
||||
return -1;
|
||||
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
if(string[0] == 0)
|
||||
return -2;
|
||||
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
std::string str(string);
|
||||
size_t pos = str.find(delim);
|
||||
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||
return -3;
|
||||
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
name = str.substr(0, pos);
|
||||
value = str.substr(pos+1, std::string::npos);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets sse_version */
|
||||
void ssedetect() {
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
/* x86 or x86-64 processor */
|
||||
uint32_t r_edx, r_ecx;
|
||||
|
||||
__asm__ __volatile__(
|
||||
__asm__ __volatile__(
|
||||
#if defined(__i386__)
|
||||
"pushl %%ebx;\n\t"
|
||||
"pushl %%ebx;\n\t"
|
||||
#endif
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"mov $0x1,%%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
#if defined(__i386__)
|
||||
"popl %%ebx;\n\t"
|
||||
"popl %%ebx;\n\t"
|
||||
#endif
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
: "=d" (r_edx), "=c" (r_ecx)
|
||||
:
|
||||
: "%eax"
|
||||
#if !defined(__i386__)
|
||||
, "%ebx"
|
||||
, "%ebx"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
if (r_ecx & 0x00000200) {
|
||||
sseversion = 35; /* SSSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||
} else if (r_ecx & 0x00000001) {
|
||||
sseversion = 30; /* SSE3 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||
} else if (r_edx & 0x04000000) {
|
||||
sseversion = 20; /* SSE2 */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||
} else if (r_edx & 0x02000000) {
|
||||
sseversion = 10; /* SSE */
|
||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||
} else {
|
||||
sseversion = 0;
|
||||
Debug(1,"Detected a x86\\x86-64 processor");
|
||||
}
|
||||
|
||||
#else
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||
sseversion = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
|
|||
#endif
|
||||
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||
if(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
if(bytes > 128) {
|
||||
unsigned int remainder = bytes % 128;
|
||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
__asm__ __volatile__(
|
||||
"sse2_copy_iter:\n\t"
|
||||
"movdqa (%0),%%xmm0\n\t"
|
||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||
"movntdq %%xmm0,(%1)\n\t"
|
||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||
"add $0x80, %0\n\t"
|
||||
"add $0x80, %1\n\t"
|
||||
"cmp %2, %0\n\t"
|
||||
"jb sse2_copy_iter\n\t"
|
||||
"test %3, %3\n\t"
|
||||
"jz sse2_copy_finish\n\t"
|
||||
"cld\n\t"
|
||||
"rep movsb\n\t"
|
||||
"sse2_copy_finish:\n\t"
|
||||
:
|
||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||
);
|
||||
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
} else {
|
||||
/* Standard memcpy */
|
||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||
}
|
||||
#else
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
/* Non x86\x86-64 platform, use memcpy */
|
||||
memcpy(dest,src,bytes);
|
||||
#endif
|
||||
return dest;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||
} else {
|
||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
|
|||
|
||||
inline int max( int a, int b )
|
||||
{
|
||||
return( a>=b?a:b );
|
||||
return( a>=b?a:b );
|
||||
}
|
||||
|
||||
inline int min( int a, int b )
|
||||
{
|
||||
return( a<=b?a:b );
|
||||
return( a<=b?a:b );
|
||||
}
|
||||
|
||||
void ssedetect();
|
||||
|
|
1824
src/zm_zone.cpp
1824
src/zm_zone.cpp
File diff suppressed because it is too large
Load Diff
226
src/zm_zone.h
226
src/zm_zone.h
|
@ -35,141 +35,141 @@ class Monitor;
|
|||
class Zone
|
||||
{
|
||||
protected:
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
struct Range
|
||||
{
|
||||
int lo_x;
|
||||
int hi_x;
|
||||
int off_x;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||
|
||||
protected:
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
// Inputs
|
||||
Monitor *monitor;
|
||||
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
int id;
|
||||
char *label;
|
||||
ZoneType type;
|
||||
Polygon polygon;
|
||||
Rgb alarm_rgb;
|
||||
CheckMethod check_method;
|
||||
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
int min_pixel_threshold;
|
||||
int max_pixel_threshold;
|
||||
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
int min_alarm_pixels;
|
||||
int max_alarm_pixels;
|
||||
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
Coord filter_box;
|
||||
int min_filter_pixels;
|
||||
int max_filter_pixels;
|
||||
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
int min_blob_pixels;
|
||||
int max_blob_pixels;
|
||||
int min_blobs;
|
||||
int max_blobs;
|
||||
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
int overload_frames;
|
||||
int extend_alarm_frames;
|
||||
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
// Outputs/Statistics
|
||||
bool alarmed;
|
||||
int pixel_diff;
|
||||
unsigned int alarm_pixels;
|
||||
int alarm_filter_pixels;
|
||||
int alarm_blob_pixels;
|
||||
int alarm_blobs;
|
||||
int min_blob_size;
|
||||
int max_blob_size;
|
||||
Box alarm_box;
|
||||
Coord alarm_centre;
|
||||
unsigned int score;
|
||||
Image *pg_image;
|
||||
Range *ranges;
|
||||
Image *image;
|
||||
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
int overload_count;
|
||||
int extend_alarm_count;
|
||||
|
||||
protected:
|
||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||
|
||||
public:
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||
{
|
||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
public:
|
||||
~Zone();
|
||||
~Zone();
|
||||
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
inline int Id() const { return( id ); }
|
||||
inline const char *Label() const { return( label ); }
|
||||
inline ZoneType Type() const { return( type ); }
|
||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||
inline const Image *AlarmImage() const { return( image ); }
|
||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||
inline bool Alarmed() const { return( alarmed ); }
|
||||
inline void SetAlarm() { alarmed = true; }
|
||||
inline void ClearAlarm() { alarmed = false; }
|
||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||
inline unsigned int Score() const { return( score ); }
|
||||
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
inline void ResetStats()
|
||||
{
|
||||
alarmed = false;
|
||||
pixel_diff = 0;
|
||||
alarm_pixels = 0;
|
||||
alarm_filter_pixels = 0;
|
||||
alarm_blob_pixels = 0;
|
||||
alarm_blobs = 0;
|
||||
min_blob_size = 0;
|
||||
max_blob_size = 0;
|
||||
score = 0;
|
||||
}
|
||||
void RecordStats( const Event *event );
|
||||
bool CheckAlarms( const Image *delta_image );
|
||||
bool DumpSettings( char *output, bool verbose );
|
||||
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||
static int Load( Monitor *monitor, Zone **&zones );
|
||||
//=================================================
|
||||
bool CheckOverloadCount();
|
||||
int GetOverloadCount();
|
||||
void SetOverloadCount(int nOverCount);
|
||||
int GetOverloadFrames();
|
||||
//=================================================
|
||||
bool CheckExtendAlarmCount();
|
||||
int GetExtendAlarmCount();
|
||||
void SetExtendAlarmCount(int nOverCount);
|
||||
int GetExtendAlarmFrames();
|
||||
void SetScore(unsigned int nScore);
|
||||
void SetAlarmImage(const Image* srcImage);
|
||||
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
inline const Image *getPgImage() const { return( pg_image ); }
|
||||
inline const Range *getRanges() const { return( ranges ); }
|
||||
|
||||
};
|
||||
|
||||
|
|
238
src/zma.cpp
238
src/zma.cpp
|
@ -41,9 +41,9 @@ behind.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -59,146 +59,146 @@ behind.
|
|||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
int id = -1;
|
||||
int id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( log_id_string );
|
||||
logInit( log_id_string );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||
|
||||
if ( monitor )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
if ( monitor )
|
||||
{
|
||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
if ( config.opt_frame_server )
|
||||
{
|
||||
Event::OpenFrameSocket( monitor->Id() );
|
||||
}
|
||||
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultHupHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||
time_t last_analysis_update_time, cur_time;
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = time( 0 );
|
||||
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
while( !zm_terminate )
|
||||
{
|
||||
// Process the next image
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
// Some periodic updates are required for variable capturing framerate
|
||||
if ( analysis_update_delay )
|
||||
{
|
||||
cur_time = time( 0 );
|
||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||
{
|
||||
analysis_rate = monitor->GetAnalysisRate();
|
||||
monitor->UpdateAdaptiveSkip();
|
||||
last_analysis_update_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
if ( !monitor->Analyse() )
|
||||
{
|
||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||
}
|
||||
else if ( analysis_rate )
|
||||
{
|
||||
usleep( analysis_rate );
|
||||
}
|
||||
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
if ( zm_reload )
|
||||
{
|
||||
monitor->Reload();
|
||||
zm_reload = false;
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
delete monitor;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( 0 );
|
||||
}
|
||||
|
|
508
src/zmc.cpp
508
src/zmc.cpp
|
@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-f, --file <file_path> - For local images, jpg file to access.
|
||||
-m, --monitor_id - ID of the monitor to analyse
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
|
|||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
#if defined(BSD)
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||
#else
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||
#endif
|
||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
const char *device = "";
|
||||
const char *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
const char *device = "";
|
||||
const char *protocol = "";
|
||||
const char *host = "";
|
||||
const char *port = "";
|
||||
const char *path = "";
|
||||
const char *file = "";
|
||||
int monitor_id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"device", 1, 0, 'd'},
|
||||
{"protocol", 1, 0, 'r'},
|
||||
{"host", 1, 0, 'H'},
|
||||
{"port", 1, 0, 'P'},
|
||||
{"path", 1, 0, 'p'},
|
||||
{"file", 1, 0, 'f'},
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
host = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
path = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
file = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
monitor_id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||
if ( modes > 1 )
|
||||
{
|
||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( modes < 1 )
|
||||
{
|
||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||
}
|
||||
char log_id_string[32] = "";
|
||||
if ( device[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( device, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||
}
|
||||
else if ( host[0] )
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
const char *slash_ptr = strrchr( file, '/' );
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||
}
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( log_id_string );
|
||||
logInit( log_id_string );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
Monitor **monitors = 0;
|
||||
int n_monitors = 0;
|
||||
Monitor **monitors = 0;
|
||||
int n_monitors = 0;
|
||||
#if ZM_HAS_V4L
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
if ( device[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
#endif // ZM_HAS_V4L
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
if ( host[0] )
|
||||
{
|
||||
if ( !port )
|
||||
port = "80";
|
||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else if ( file[0] )
|
||||
{
|
||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||
}
|
||||
else
|
||||
{
|
||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||
if ( monitor )
|
||||
{
|
||||
monitors = new Monitor *[1];
|
||||
monitors[0] = monitor;
|
||||
n_monitors = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
if ( !n_monitors )
|
||||
{
|
||||
Error( "No monitors found" );
|
||||
exit ( -1 );
|
||||
}
|
||||
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
Info( "Starting Capture version %s", ZM_VERSION );
|
||||
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
sigaddset( &block_set, SIGUSR1 );
|
||||
sigaddset( &block_set, SIGUSR2 );
|
||||
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( monitors[0]->PrimeCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to prime capture of initial monitor" );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
long *capture_delays = new long[n_monitors];
|
||||
long *alarm_capture_delays = new long[n_monitors];
|
||||
long *next_delays = new long[n_monitors];
|
||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
int result = 0;
|
||||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while( !zm_terminate )
|
||||
{
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
long min_delay = MAXINT;
|
||||
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
gettimeofday( &now, NULL );
|
||||
for ( int j = 0; j < n_monitors; j++ )
|
||||
{
|
||||
if ( last_capture_times[j].tv_sec )
|
||||
{
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||
else
|
||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||
if ( next_delays[j] < 0 )
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
next_delays[j] = 0;
|
||||
}
|
||||
if ( next_delays[j] <= min_delay )
|
||||
{
|
||||
min_delay = next_delays[j];
|
||||
}
|
||||
}
|
||||
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||
{
|
||||
if ( monitors[i]->PreCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->Capture() < 0 )
|
||||
{
|
||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->PostCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||
{
|
||||
if ( monitors[i]->PreCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->Capture() < 0 )
|
||||
{
|
||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
if ( monitors[i]->PostCapture() < 0 )
|
||||
{
|
||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||
zm_terminate = true;
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
if ( next_delays[i] > 0 )
|
||||
{
|
||||
gettimeofday( &now, NULL );
|
||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||
long sleep_time = next_delays[i]-delta_time.delta;
|
||||
if ( sleep_time > 0 )
|
||||
{
|
||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||
}
|
||||
}
|
||||
gettimeofday( &(last_capture_times[i]), NULL );
|
||||
}
|
||||
}
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
for ( int i = 0; i < n_monitors; i++ )
|
||||
{
|
||||
delete monitors[i];
|
||||
}
|
||||
delete [] monitors;
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] next_delays;
|
||||
delete [] last_capture_times;
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return( result );
|
||||
return( result );
|
||||
}
|
||||
|
|
448
src/zmf.cpp
448
src/zmf.cpp
|
@ -42,9 +42,9 @@ them itself.
|
|||
|
||||
=head1 OPTIONS
|
||||
|
||||
-m, --monitor_id - ID of the monitor to use
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
-m, --monitor_id - ID of the monitor to use
|
||||
-h, --help - Display usage information
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -73,278 +73,278 @@ them itself.
|
|||
|
||||
int OpenSocket( int monitor_id )
|
||||
{
|
||||
int sd = socket( AF_UNIX, SOCK_STREAM, 0);
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Error( "Can't create socket: %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
int sd = socket( AF_UNIX, SOCK_STREAM, 0);
|
||||
if ( sd < 0 )
|
||||
{
|
||||
Error( "Can't create socket: %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
char sock_path[PATH_MAX] = "";
|
||||
snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id );
|
||||
if ( unlink( sock_path ) < 0 )
|
||||
{
|
||||
Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) );
|
||||
}
|
||||
char sock_path[PATH_MAX] = "";
|
||||
snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id );
|
||||
if ( unlink( sock_path ) < 0 )
|
||||
{
|
||||
Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) );
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) );
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) );
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 )
|
||||
{
|
||||
Error( "Can't bind: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 )
|
||||
{
|
||||
Error( "Can't bind: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
if ( listen( sd, SOMAXCONN ) < 0 )
|
||||
{
|
||||
Error( "Can't listen: %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
if ( listen( sd, SOMAXCONN ) < 0 )
|
||||
{
|
||||
Error( "Can't listen: %s", strerror(errno) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
struct sockaddr_un rem_addr;
|
||||
socklen_t rem_addr_len = sizeof(rem_addr);
|
||||
int new_sd = -1;
|
||||
if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 )
|
||||
{
|
||||
Error( "Can't accept: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
close( sd );
|
||||
struct sockaddr_un rem_addr;
|
||||
socklen_t rem_addr_len = sizeof(rem_addr);
|
||||
int new_sd = -1;
|
||||
if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 )
|
||||
{
|
||||
Error( "Can't accept: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
close( sd );
|
||||
|
||||
sd = new_sd;
|
||||
sd = new_sd;
|
||||
|
||||
Info( "Frame server socket open, awaiting images" );
|
||||
return( sd );
|
||||
Info( "Frame server socket open, awaiting images" );
|
||||
return( sd );
|
||||
}
|
||||
|
||||
int ReopenSocket( int &sd, int monitor_id )
|
||||
{
|
||||
close( sd );
|
||||
return( sd = OpenSocket( monitor_id ) );
|
||||
close( sd );
|
||||
return( sd = OpenSocket( monitor_id ) );
|
||||
}
|
||||
|
||||
void Usage()
|
||||
{
|
||||
fprintf( stderr, "zmf -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
fprintf( stderr, "zmf -m <monitor_id>\n" );
|
||||
fprintf( stderr, "Options:\n" );
|
||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||
fprintf( stderr, " -h, --help : This screen\n" );
|
||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
int id = -1;
|
||||
int id = -1;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
static struct option long_options[] = {
|
||||
{"monitor", 1, 0, 'm'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
while (1)
|
||||
{
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case 'm':
|
||||
id = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage();
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << ZM_VERSION << "\n";
|
||||
exit(0);
|
||||
default:
|
||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
if (optind < argc)
|
||||
{
|
||||
fprintf( stderr, "Extraneous options, " );
|
||||
while (optind < argc)
|
||||
printf ("%s ", argv[optind++]);
|
||||
printf ("\n");
|
||||
Usage();
|
||||
}
|
||||
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
if ( id < 0 )
|
||||
{
|
||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||
Usage();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "m%d", id );
|
||||
char log_id_string[16];
|
||||
snprintf( log_id_string, sizeof(log_id_string), "m%d", id );
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( "zmf" );
|
||||
logInit( "zmf" );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY );
|
||||
Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY );
|
||||
|
||||
if ( !monitor )
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( !monitor )
|
||||
{
|
||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
char capt_path[PATH_MAX];
|
||||
char anal_path[PATH_MAX];
|
||||
snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||
snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
char capt_path[PATH_MAX];
|
||||
char anal_path[PATH_MAX];
|
||||
snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||
snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
sigset_t block_set;
|
||||
sigemptyset( &block_set );
|
||||
|
||||
int sd = OpenSocket( monitor->Id() );
|
||||
int sd = OpenSocket( monitor->Id() );
|
||||
|
||||
FrameHeader frame_header = { 0, 0, false, 0 };
|
||||
//unsigned char *image_data = 0;
|
||||
FrameHeader frame_header = { 0, 0, false, 0 };
|
||||
//unsigned char *image_data = 0;
|
||||
|
||||
fd_set rfds;
|
||||
fd_set rfds;
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
while( 1 )
|
||||
{
|
||||
struct timeval temp_timeout = timeout;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
while( 1 )
|
||||
{
|
||||
struct timeval temp_timeout = timeout;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sd, &rfds);
|
||||
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
|
||||
if( n_found == 0 )
|
||||
{
|
||||
Debug( 1, "Select timed out" );
|
||||
continue;
|
||||
}
|
||||
else if ( n_found < 0)
|
||||
{
|
||||
Error( "Select error: %s", strerror(errno) );
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sd, &rfds);
|
||||
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
|
||||
if( n_found == 0 )
|
||||
{
|
||||
Debug( 1, "Select timed out" );
|
||||
continue;
|
||||
}
|
||||
else if ( n_found < 0)
|
||||
{
|
||||
Error( "Select error: %s", strerror(errno) );
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||
|
||||
int n_bytes = read( sd, &frame_header, sizeof(frame_header) );
|
||||
if ( n_bytes != sizeof(frame_header) )
|
||||
{
|
||||
if ( n_bytes < 0 )
|
||||
{
|
||||
Error( "Can't read frame header: %s", strerror(errno) );
|
||||
}
|
||||
else if ( n_bytes > 0 )
|
||||
{
|
||||
Error( "Incomplete read of frame header, %d bytes only", n_bytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Socket closed at remote end" );
|
||||
}
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length );
|
||||
static unsigned char image_data[ZM_MAX_IMAGE_SIZE];
|
||||
int n_bytes = read( sd, &frame_header, sizeof(frame_header) );
|
||||
if ( n_bytes != sizeof(frame_header) )
|
||||
{
|
||||
if ( n_bytes < 0 )
|
||||
{
|
||||
Error( "Can't read frame header: %s", strerror(errno) );
|
||||
}
|
||||
else if ( n_bytes > 0 )
|
||||
{
|
||||
Error( "Incomplete read of frame header, %d bytes only", n_bytes );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Socket closed at remote end" );
|
||||
}
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length );
|
||||
static unsigned char image_data[ZM_MAX_IMAGE_SIZE];
|
||||
|
||||
// Read for pipe and loop until bytes expected have been read or an error occurs
|
||||
int bytes_read = 0;
|
||||
do
|
||||
{
|
||||
n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read );
|
||||
if (n_bytes < 0) break; // break on error
|
||||
if (n_bytes < (int)frame_header.image_length)
|
||||
{
|
||||
// print some informational messages
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
|
||||
}
|
||||
else if (bytes_read+n_bytes == (int)frame_header.image_length)
|
||||
{
|
||||
Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
|
||||
}
|
||||
}
|
||||
bytes_read+= n_bytes;
|
||||
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
|
||||
|
||||
// Print errors if there was a problem
|
||||
if ( n_bytes < 1 )
|
||||
// Read for pipe and loop until bytes expected have been read or an error occurs
|
||||
int bytes_read = 0;
|
||||
do
|
||||
{
|
||||
n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read );
|
||||
if (n_bytes < 0) break; // break on error
|
||||
if (n_bytes < (int)frame_header.image_length)
|
||||
{
|
||||
// print some informational messages
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length);
|
||||
if ( n_bytes < 0 )
|
||||
{
|
||||
Error( "Can't read frame image data: %s", strerror(errno) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Socket closed at remote end" );
|
||||
}
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
|
||||
static char subpath[PATH_MAX] = "";
|
||||
if ( config.use_deep_storage )
|
||||
Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
|
||||
}
|
||||
else if (bytes_read+n_bytes == (int)frame_header.image_length)
|
||||
{
|
||||
struct tm *time = localtime( &frame_header.event_time );
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id );
|
||||
Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
|
||||
}
|
||||
}
|
||||
bytes_read+= n_bytes;
|
||||
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
|
||||
|
||||
static char path[PATH_MAX] = "";
|
||||
snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id );
|
||||
Debug( 1, "Got image, writing to %s", path );
|
||||
// Print errors if there was a problem
|
||||
if ( n_bytes < 1 )
|
||||
{
|
||||
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length);
|
||||
if ( n_bytes < 0 )
|
||||
{
|
||||
Error( "Can't read frame image data: %s", strerror(errno) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Socket closed at remote end" );
|
||||
}
|
||||
ReopenSocket( sd, monitor->Id() );
|
||||
continue;
|
||||
}
|
||||
|
||||
FILE *fd = 0;
|
||||
if ( (fd = fopen( path, "w" )) < 0 )
|
||||
{
|
||||
Error( "Can't fopen '%s': %s", path, strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) )
|
||||
{
|
||||
Error( "Can't fwrite image data: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
fclose( fd );
|
||||
static char subpath[PATH_MAX] = "";
|
||||
if ( config.use_deep_storage )
|
||||
{
|
||||
struct tm *time = localtime( &frame_header.event_time );
|
||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id );
|
||||
}
|
||||
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
static char path[PATH_MAX] = "";
|
||||
snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id );
|
||||
Debug( 1, "Got image, writing to %s", path );
|
||||
|
||||
FILE *fd = 0;
|
||||
if ( (fd = fopen( path, "w" )) < 0 )
|
||||
{
|
||||
Error( "Can't fopen '%s': %s", path, strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) )
|
||||
{
|
||||
Error( "Can't fwrite image data: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
}
|
||||
fclose( fd );
|
||||
|
||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||
}
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
}
|
||||
|
|
10
src/zmf.h
10
src/zmf.h
|
@ -22,11 +22,11 @@
|
|||
|
||||
struct FrameHeader
|
||||
{
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
unsigned long event_id;
|
||||
time_t event_time;
|
||||
unsigned long frame_id;
|
||||
bool alarm_frame;
|
||||
unsigned long image_length;
|
||||
};
|
||||
|
||||
#endif // ZMFILE_H
|
||||
|
|
592
src/zms.cpp
592
src/zms.cpp
|
@ -28,318 +28,318 @@
|
|||
|
||||
bool ValidateAccess( User *user, int mon_id )
|
||||
{
|
||||
bool allowed = true;
|
||||
bool allowed = true;
|
||||
|
||||
if ( mon_id > 0 )
|
||||
{
|
||||
if ( user->getStream() < User::PERM_VIEW )
|
||||
allowed = false;
|
||||
if ( !user->canAccess( mon_id ) )
|
||||
allowed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( user->getEvents() < User::PERM_VIEW )
|
||||
allowed = false;
|
||||
}
|
||||
if ( !allowed )
|
||||
{
|
||||
Error( "Error, insufficient privileges for requested action" );
|
||||
exit( -1 );
|
||||
}
|
||||
return( allowed );
|
||||
if ( mon_id > 0 )
|
||||
{
|
||||
if ( user->getStream() < User::PERM_VIEW )
|
||||
allowed = false;
|
||||
if ( !user->canAccess( mon_id ) )
|
||||
allowed = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( user->getEvents() < User::PERM_VIEW )
|
||||
allowed = false;
|
||||
}
|
||||
if ( !allowed )
|
||||
{
|
||||
Error( "Error, insufficient privileges for requested action" );
|
||||
exit( -1 );
|
||||
}
|
||||
return( allowed );
|
||||
}
|
||||
|
||||
int main( int argc, const char *argv[] )
|
||||
{
|
||||
self = argv[0];
|
||||
self = argv[0];
|
||||
|
||||
srand( getpid() * time( 0 ) );
|
||||
srand( getpid() * time( 0 ) );
|
||||
|
||||
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
|
||||
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
||||
char format[32] = "";
|
||||
int monitor_id = 0;
|
||||
time_t event_time = 0;
|
||||
int event_id = 0;
|
||||
int frame_id = 1;
|
||||
unsigned int scale = 100;
|
||||
unsigned int rate = 100;
|
||||
double maxfps = 10.0;
|
||||
unsigned int bitrate = 100000;
|
||||
unsigned int ttl = 0;
|
||||
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
|
||||
char username[64] = "";
|
||||
char password[64] = "";
|
||||
char auth[64] = "";
|
||||
unsigned int connkey = 0;
|
||||
unsigned int playback_buffer = 0;
|
||||
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
|
||||
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
||||
char format[32] = "";
|
||||
int monitor_id = 0;
|
||||
time_t event_time = 0;
|
||||
int event_id = 0;
|
||||
int frame_id = 1;
|
||||
unsigned int scale = 100;
|
||||
unsigned int rate = 100;
|
||||
double maxfps = 10.0;
|
||||
unsigned int bitrate = 100000;
|
||||
unsigned int ttl = 0;
|
||||
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
|
||||
char username[64] = "";
|
||||
char password[64] = "";
|
||||
char auth[64] = "";
|
||||
unsigned int connkey = 0;
|
||||
unsigned int playback_buffer = 0;
|
||||
|
||||
bool nph = false;
|
||||
const char *basename = strrchr( argv[0], '/' );
|
||||
if (basename) //if we found a / lets skip past it
|
||||
basename++;
|
||||
else //argv[0] will not always contain the full path, but rather just the script name
|
||||
basename = argv[0];
|
||||
const char *nph_prefix = "nph-";
|
||||
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
|
||||
{
|
||||
nph = true;
|
||||
}
|
||||
bool nph = false;
|
||||
const char *basename = strrchr( argv[0], '/' );
|
||||
if (basename) //if we found a / lets skip past it
|
||||
basename++;
|
||||
else //argv[0] will not always contain the full path, but rather just the script name
|
||||
basename = argv[0];
|
||||
const char *nph_prefix = "nph-";
|
||||
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
|
||||
{
|
||||
nph = true;
|
||||
}
|
||||
|
||||
zmLoadConfig();
|
||||
zmLoadConfig();
|
||||
|
||||
logInit( "zms" );
|
||||
logInit( "zms" );
|
||||
|
||||
ssedetect();
|
||||
ssedetect();
|
||||
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
const char *query = getenv( "QUERY_STRING" );
|
||||
if ( query )
|
||||
{
|
||||
Debug( 1, "Query: %s", query );
|
||||
const char *query = getenv( "QUERY_STRING" );
|
||||
if ( query )
|
||||
{
|
||||
Debug( 1, "Query: %s", query );
|
||||
|
||||
char temp_query[1024];
|
||||
strncpy( temp_query, query, sizeof(temp_query) );
|
||||
char *q_ptr = temp_query;
|
||||
char *parms[16]; // Shouldn't be more than this
|
||||
int parm_no = 0;
|
||||
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
|
||||
{
|
||||
parm_no++;
|
||||
q_ptr = NULL;
|
||||
}
|
||||
|
||||
for ( int p = 0; p < parm_no; p++ )
|
||||
{
|
||||
char *name = strtok( parms[p], "=" );
|
||||
char *value = strtok( NULL, "=" );
|
||||
if ( !value )
|
||||
value = (char *)"";
|
||||
if ( !strcmp( name, "source" ) )
|
||||
{
|
||||
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
|
||||
}
|
||||
else if ( !strcmp( name, "mode" ) )
|
||||
{
|
||||
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
||||
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
||||
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
||||
}
|
||||
else if ( !strcmp( name, "format" ) )
|
||||
strncpy( format, value, sizeof(format) );
|
||||
else if ( !strcmp( name, "monitor" ) )
|
||||
monitor_id = atoi( value );
|
||||
else if ( !strcmp( name, "time" ) )
|
||||
event_time = atoi( value );
|
||||
else if ( !strcmp( name, "event" ) )
|
||||
event_id = strtoull( value, (char **)NULL, 10 );
|
||||
else if ( !strcmp( name, "frame" ) )
|
||||
frame_id = strtoull( value, (char **)NULL, 10 );
|
||||
else if ( !strcmp( name, "scale" ) )
|
||||
scale = atoi( value );
|
||||
else if ( !strcmp( name, "rate" ) )
|
||||
rate = atoi( value );
|
||||
else if ( !strcmp( name, "maxfps" ) )
|
||||
maxfps = atof( value );
|
||||
else if ( !strcmp( name, "bitrate" ) )
|
||||
bitrate = atoi( value );
|
||||
else if ( !strcmp( name, "ttl" ) )
|
||||
ttl = atoi(value);
|
||||
else if ( !strcmp( name, "replay" ) )
|
||||
{
|
||||
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
|
||||
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
|
||||
}
|
||||
else if ( !strcmp( name, "connkey" ) )
|
||||
connkey = atoi(value);
|
||||
else if ( !strcmp( name, "buffer" ) )
|
||||
playback_buffer = atoi(value);
|
||||
else if ( config.opt_use_auth )
|
||||
{
|
||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "user" ) )
|
||||
{
|
||||
strncpy( username, value, sizeof(username) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "auth" ) )
|
||||
{
|
||||
strncpy( auth, value, sizeof(auth) );
|
||||
}
|
||||
}
|
||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "user" ) )
|
||||
{
|
||||
strncpy( username, value, sizeof(username) );
|
||||
}
|
||||
if ( !strcmp( name, "pass" ) )
|
||||
{
|
||||
strncpy( password, value, sizeof(password) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( config.opt_use_auth )
|
||||
{
|
||||
User *user = 0;
|
||||
|
||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
||||
{
|
||||
if ( *username )
|
||||
{
|
||||
user = zmLoadUser( username );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||
{
|
||||
if ( *auth )
|
||||
{
|
||||
user = zmLoadAuthUser( auth, config.auth_hash_ips );
|
||||
}
|
||||
}
|
||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||
{
|
||||
if ( *username && *password )
|
||||
{
|
||||
user = zmLoadUser( username, password );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !user )
|
||||
{
|
||||
Error( "Unable to authenticate user" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
}
|
||||
ValidateAccess( user, monitor_id );
|
||||
}
|
||||
|
||||
setbuf( stdout, 0 );
|
||||
if ( nph )
|
||||
{
|
||||
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
|
||||
}
|
||||
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
|
||||
|
||||
time_t now = time( 0 );
|
||||
char date_string[64];
|
||||
strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
|
||||
|
||||
fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" );
|
||||
fprintf( stdout, "Last-Modified: %s\r\n", date_string );
|
||||
fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" );
|
||||
fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" );
|
||||
fprintf( stdout, "Pragma: no-cache\r\n");
|
||||
// Removed as causing more problems than it fixed.
|
||||
//if ( !nph )
|
||||
//{
|
||||
//fprintf( stdout, "Content-Length: 0\r\n");
|
||||
//}
|
||||
|
||||
if ( source == ZMS_MONITOR )
|
||||
{
|
||||
MonitorStream stream;
|
||||
stream.setStreamScale( scale );
|
||||
stream.setStreamReplayRate( rate );
|
||||
stream.setStreamMaxFPS( maxfps );
|
||||
stream.setStreamTTL( ttl );
|
||||
stream.setStreamQueue( connkey );
|
||||
stream.setStreamBuffer( playback_buffer );
|
||||
if ( ! stream.setStreamStart( monitor_id ) ) {
|
||||
Error( "Unable to connect to zmc process for monitor %d", monitor_id );
|
||||
fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if ( mode == ZMS_JPEG )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_JPEG );
|
||||
}
|
||||
else if ( mode == ZMS_RAW )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_RAW );
|
||||
}
|
||||
else if ( mode == ZMS_ZIP )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_ZIP );
|
||||
}
|
||||
else if ( mode == ZMS_SINGLE )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_SINGLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
stream.setStreamFormat( format );
|
||||
stream.setStreamBitrate( bitrate );
|
||||
stream.setStreamType( MonitorStream::STREAM_MPEG );
|
||||
#else // HAVE_LIBAVCODEC
|
||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
stream.runStream();
|
||||
}
|
||||
else if ( source == ZMS_EVENT )
|
||||
char temp_query[1024];
|
||||
strncpy( temp_query, query, sizeof(temp_query) );
|
||||
char *q_ptr = temp_query;
|
||||
char *parms[16]; // Shouldn't be more than this
|
||||
int parm_no = 0;
|
||||
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
|
||||
{
|
||||
EventStream stream;
|
||||
stream.setStreamScale( scale );
|
||||
stream.setStreamReplayRate( rate );
|
||||
stream.setStreamMaxFPS( maxfps );
|
||||
stream.setStreamMode( replay );
|
||||
stream.setStreamQueue( connkey );
|
||||
if ( monitor_id && event_time )
|
||||
{
|
||||
stream.setStreamStart( monitor_id, event_time );
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.setStreamStart( event_id, frame_id );
|
||||
}
|
||||
if ( mode == ZMS_JPEG )
|
||||
{
|
||||
stream.setStreamType( EventStream::STREAM_JPEG );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
stream.setStreamFormat( format );
|
||||
stream.setStreamBitrate( bitrate );
|
||||
stream.setStreamType( EventStream::STREAM_MPEG );
|
||||
#else // HAVE_LIBAVCODEC
|
||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
stream.runStream();
|
||||
parm_no++;
|
||||
q_ptr = NULL;
|
||||
}
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
for ( int p = 0; p < parm_no; p++ )
|
||||
{
|
||||
char *name = strtok( parms[p], "=" );
|
||||
char *value = strtok( NULL, "=" );
|
||||
if ( !value )
|
||||
value = (char *)"";
|
||||
if ( !strcmp( name, "source" ) )
|
||||
{
|
||||
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
|
||||
}
|
||||
else if ( !strcmp( name, "mode" ) )
|
||||
{
|
||||
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
||||
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
||||
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
||||
}
|
||||
else if ( !strcmp( name, "format" ) )
|
||||
strncpy( format, value, sizeof(format) );
|
||||
else if ( !strcmp( name, "monitor" ) )
|
||||
monitor_id = atoi( value );
|
||||
else if ( !strcmp( name, "time" ) )
|
||||
event_time = atoi( value );
|
||||
else if ( !strcmp( name, "event" ) )
|
||||
event_id = strtoull( value, (char **)NULL, 10 );
|
||||
else if ( !strcmp( name, "frame" ) )
|
||||
frame_id = strtoull( value, (char **)NULL, 10 );
|
||||
else if ( !strcmp( name, "scale" ) )
|
||||
scale = atoi( value );
|
||||
else if ( !strcmp( name, "rate" ) )
|
||||
rate = atoi( value );
|
||||
else if ( !strcmp( name, "maxfps" ) )
|
||||
maxfps = atof( value );
|
||||
else if ( !strcmp( name, "bitrate" ) )
|
||||
bitrate = atoi( value );
|
||||
else if ( !strcmp( name, "ttl" ) )
|
||||
ttl = atoi(value);
|
||||
else if ( !strcmp( name, "replay" ) )
|
||||
{
|
||||
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
|
||||
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
|
||||
}
|
||||
else if ( !strcmp( name, "connkey" ) )
|
||||
connkey = atoi(value);
|
||||
else if ( !strcmp( name, "buffer" ) )
|
||||
playback_buffer = atoi(value);
|
||||
else if ( config.opt_use_auth )
|
||||
{
|
||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "user" ) )
|
||||
{
|
||||
strncpy( username, value, sizeof(username) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "auth" ) )
|
||||
{
|
||||
strncpy( auth, value, sizeof(auth) );
|
||||
}
|
||||
}
|
||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||
{
|
||||
if ( !strcmp( name, "user" ) )
|
||||
{
|
||||
strncpy( username, value, sizeof(username) );
|
||||
}
|
||||
if ( !strcmp( name, "pass" ) )
|
||||
{
|
||||
strncpy( password, value, sizeof(password) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
if ( config.opt_use_auth )
|
||||
{
|
||||
User *user = 0;
|
||||
|
||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
||||
{
|
||||
if ( *username )
|
||||
{
|
||||
user = zmLoadUser( username );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
||||
{
|
||||
if ( *auth )
|
||||
{
|
||||
user = zmLoadAuthUser( auth, config.auth_hash_ips );
|
||||
}
|
||||
}
|
||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
||||
{
|
||||
if ( *username && *password )
|
||||
{
|
||||
user = zmLoadUser( username, password );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !user )
|
||||
{
|
||||
Error( "Unable to authenticate user" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
}
|
||||
ValidateAccess( user, monitor_id );
|
||||
}
|
||||
|
||||
setbuf( stdout, 0 );
|
||||
if ( nph )
|
||||
{
|
||||
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
|
||||
}
|
||||
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
|
||||
|
||||
time_t now = time( 0 );
|
||||
char date_string[64];
|
||||
strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
|
||||
|
||||
fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" );
|
||||
fprintf( stdout, "Last-Modified: %s\r\n", date_string );
|
||||
fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" );
|
||||
fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" );
|
||||
fprintf( stdout, "Pragma: no-cache\r\n");
|
||||
// Removed as causing more problems than it fixed.
|
||||
//if ( !nph )
|
||||
//{
|
||||
//fprintf( stdout, "Content-Length: 0\r\n");
|
||||
//}
|
||||
|
||||
if ( source == ZMS_MONITOR )
|
||||
{
|
||||
MonitorStream stream;
|
||||
stream.setStreamScale( scale );
|
||||
stream.setStreamReplayRate( rate );
|
||||
stream.setStreamMaxFPS( maxfps );
|
||||
stream.setStreamTTL( ttl );
|
||||
stream.setStreamQueue( connkey );
|
||||
stream.setStreamBuffer( playback_buffer );
|
||||
if ( ! stream.setStreamStart( monitor_id ) ) {
|
||||
Error( "Unable to connect to zmc process for monitor %d", monitor_id );
|
||||
fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if ( mode == ZMS_JPEG )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_JPEG );
|
||||
}
|
||||
else if ( mode == ZMS_RAW )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_RAW );
|
||||
}
|
||||
else if ( mode == ZMS_ZIP )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_ZIP );
|
||||
}
|
||||
else if ( mode == ZMS_SINGLE )
|
||||
{
|
||||
stream.setStreamType( MonitorStream::STREAM_SINGLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
stream.setStreamFormat( format );
|
||||
stream.setStreamBitrate( bitrate );
|
||||
stream.setStreamType( MonitorStream::STREAM_MPEG );
|
||||
#else // HAVE_LIBAVCODEC
|
||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
stream.runStream();
|
||||
}
|
||||
else if ( source == ZMS_EVENT )
|
||||
{
|
||||
EventStream stream;
|
||||
stream.setStreamScale( scale );
|
||||
stream.setStreamReplayRate( rate );
|
||||
stream.setStreamMaxFPS( maxfps );
|
||||
stream.setStreamMode( replay );
|
||||
stream.setStreamQueue( connkey );
|
||||
if ( monitor_id && event_time )
|
||||
{
|
||||
stream.setStreamStart( monitor_id, event_time );
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.setStreamStart( event_id, frame_id );
|
||||
}
|
||||
if ( mode == ZMS_JPEG )
|
||||
{
|
||||
stream.setStreamType( EventStream::STREAM_JPEG );
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
stream.setStreamFormat( format );
|
||||
stream.setStreamBitrate( bitrate );
|
||||
stream.setStreamType( EventStream::STREAM_MPEG );
|
||||
#else // HAVE_LIBAVCODEC
|
||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" );
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
return( -1 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
}
|
||||
stream.runStream();
|
||||
}
|
||||
|
||||
logTerm();
|
||||
zmDbClose();
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
|
|
@ -50,16 +50,16 @@ 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
|
||||
-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
|
||||
|
||||
|
@ -92,161 +92,161 @@ running the eyeZm app.
|
|||
#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;
|
||||
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);
|
||||
}
|
||||
// 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;
|
||||
// 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) {
|
||||
// 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();
|
||||
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");
|
||||
}
|
||||
|
||||
logInit("zmstreamer");
|
||||
// Loading ZM configurations
|
||||
printf("Loading ZoneMinder configurations...");
|
||||
zmLoadConfig();
|
||||
printf("Done.\n");
|
||||
|
||||
ssedetect();
|
||||
logInit("zmstreamer");
|
||||
|
||||
// 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) {
|
||||
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)
|
||||
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");
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return (EXIT_SUCCESS);
|
||||
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);
|
||||
}
|
||||
|
|
1450
src/zmu.cpp
1450
src/zmu.cpp
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue