Merge pull request #1384 from ZoneMinder/tabs_to_spaces
tabs to spaces and use 2space indenting
This commit is contained in:
commit
66a3fef61e
4
src/zm.h
4
src/zm.h
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
#ifdef SOLARIS
|
#ifdef SOLARIS
|
||||||
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE
|
||||||
#include <string.h> // define strerror() and friends
|
#include <string.h> // define strerror() and friends
|
||||||
#endif
|
#endif
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
|
|
||||||
|
|
12296
src/zm_bigfont.h
12296
src/zm_bigfont.h
File diff suppressed because it is too large
Load Diff
58
src/zm_box.h
58
src/zm_box.h
|
@ -36,39 +36,39 @@
|
||||||
class Box
|
class Box
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Coord lo, hi;
|
Coord lo, hi;
|
||||||
Coord size;
|
Coord size;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Box()
|
inline Box()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { }
|
||||||
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { }
|
||||||
|
|
||||||
inline const Coord &Lo() const { return( lo ); }
|
inline const Coord &Lo() const { return( lo ); }
|
||||||
inline int LoX() const { return( lo.X() ); }
|
inline int LoX() const { return( lo.X() ); }
|
||||||
inline int LoY() const { return( lo.Y() ); }
|
inline int LoY() const { return( lo.Y() ); }
|
||||||
inline const Coord &Hi() const { return( hi ); }
|
inline const Coord &Hi() const { return( hi ); }
|
||||||
inline int HiX() const { return( hi.X() ); }
|
inline int HiX() const { return( hi.X() ); }
|
||||||
inline int HiY() const { return( hi.Y() ); }
|
inline int HiY() const { return( hi.Y() ); }
|
||||||
inline const Coord &Size() const { return( size ); }
|
inline const Coord &Size() const { return( size ); }
|
||||||
inline int Width() const { return( size.X() ); }
|
inline int Width() const { return( size.X() ); }
|
||||||
inline int Height() const { return( size.Y() ); }
|
inline int Height() const { return( size.Y() ); }
|
||||||
inline int Area() const { return( size.X()*size.Y() ); }
|
inline int Area() const { return( size.X()*size.Y() ); }
|
||||||
|
|
||||||
inline const Coord Centre() const
|
inline const Coord Centre() const
|
||||||
{
|
{
|
||||||
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
int mid_x = int(round(lo.X()+(size.X()/2.0)));
|
||||||
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
int mid_y = int(round(lo.Y()+(size.Y()/2.0)));
|
||||||
return( Coord( mid_x, mid_y ) );
|
return( Coord( mid_x, mid_y ) );
|
||||||
}
|
}
|
||||||
inline bool Inside( const Coord &coord ) const
|
inline bool Inside( const Coord &coord ) const
|
||||||
{
|
{
|
||||||
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_BOX_H
|
#endif // ZM_BOX_H
|
||||||
|
|
|
@ -25,57 +25,57 @@
|
||||||
|
|
||||||
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize )
|
||||||
{
|
{
|
||||||
if ( mAllocation < pSize )
|
if ( mAllocation < pSize )
|
||||||
{
|
{
|
||||||
delete[] mStorage;
|
delete[] mStorage;
|
||||||
mAllocation = pSize;
|
mAllocation = pSize;
|
||||||
mHead = mStorage = new unsigned char[pSize];
|
mHead = mStorage = new unsigned char[pSize];
|
||||||
}
|
}
|
||||||
mSize = pSize;
|
mSize = pSize;
|
||||||
memcpy( mStorage, pStorage, mSize );
|
memcpy( mStorage, pStorage, mSize );
|
||||||
mHead = mStorage;
|
mHead = mStorage;
|
||||||
mTail = mHead + mSize;
|
mTail = mHead + mSize;
|
||||||
return( mSize );
|
return( mSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Buffer::expand( unsigned int count )
|
unsigned int Buffer::expand( unsigned int count )
|
||||||
{
|
{
|
||||||
int spare = mAllocation - mSize;
|
int spare = mAllocation - mSize;
|
||||||
int headSpace = mHead - mStorage;
|
int headSpace = mHead - mStorage;
|
||||||
int tailSpace = spare - headSpace;
|
int tailSpace = spare - headSpace;
|
||||||
int width = mTail - mHead;
|
int width = mTail - mHead;
|
||||||
if ( spare > (int)count )
|
if ( spare > (int)count )
|
||||||
|
{
|
||||||
|
if ( tailSpace < (int)count )
|
||||||
{
|
{
|
||||||
if ( tailSpace < (int)count )
|
memmove( mStorage, mHead, mSize );
|
||||||
{
|
mHead = mStorage;
|
||||||
memmove( mStorage, mHead, mSize );
|
mTail = mHead + width;
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mAllocation += count;
|
||||||
|
unsigned char *newStorage = new unsigned char[mAllocation];
|
||||||
|
if ( mStorage )
|
||||||
{
|
{
|
||||||
mAllocation += count;
|
memcpy( newStorage, mHead, mSize );
|
||||||
unsigned char *newStorage = new unsigned char[mAllocation];
|
delete[] mStorage;
|
||||||
if ( mStorage )
|
|
||||||
{
|
|
||||||
memcpy( newStorage, mHead, mSize );
|
|
||||||
delete[] mStorage;
|
|
||||||
}
|
|
||||||
mStorage = newStorage;
|
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + width;
|
|
||||||
}
|
}
|
||||||
return( mSize );
|
mStorage = newStorage;
|
||||||
|
mHead = mStorage;
|
||||||
|
mTail = mHead + width;
|
||||||
|
}
|
||||||
|
return( mSize );
|
||||||
}
|
}
|
||||||
|
|
||||||
int Buffer::read_into( int sd, unsigned int bytes ) {
|
int Buffer::read_into( int sd, unsigned int bytes ) {
|
||||||
// Make sure there is enough space
|
// Make sure there is enough space
|
||||||
this->expand(bytes);
|
this->expand(bytes);
|
||||||
int bytes_read = read( sd, mTail, bytes );
|
int bytes_read = read( sd, mTail, bytes );
|
||||||
if ( bytes_read > 0 ) {
|
if ( bytes_read > 0 ) {
|
||||||
mTail += bytes_read;
|
mTail += bytes_read;
|
||||||
mSize += bytes_read;
|
mSize += bytes_read;
|
||||||
}
|
}
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
332
src/zm_buffer.h
332
src/zm_buffer.h
|
@ -27,183 +27,183 @@
|
||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
unsigned char *mStorage;
|
unsigned char *mStorage;
|
||||||
unsigned int mAllocation;
|
unsigned int mAllocation;
|
||||||
unsigned int mSize;
|
unsigned int mSize;
|
||||||
unsigned char *mHead;
|
unsigned char *mHead;
|
||||||
unsigned char *mTail;
|
unsigned char *mTail;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mAllocation];
|
||||||
|
mTail = mHead;
|
||||||
|
}
|
||||||
|
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mSize];
|
||||||
|
memcpy( mStorage, pStorage, mSize );
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
}
|
||||||
|
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
||||||
|
{
|
||||||
|
mHead = mStorage = new unsigned char[mSize];
|
||||||
|
memcpy( mStorage, buffer.mHead, mSize );
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
}
|
||||||
|
~Buffer()
|
||||||
|
{
|
||||||
|
delete[] mStorage;
|
||||||
|
}
|
||||||
|
unsigned char *head() const { return( mHead ); }
|
||||||
|
unsigned char *tail() const { return( mTail ); }
|
||||||
|
unsigned int size() const { return( mSize ); }
|
||||||
|
bool empty() const { return( mSize == 0 ); }
|
||||||
|
unsigned int size( unsigned int pSize )
|
||||||
|
{
|
||||||
|
if ( mSize < pSize )
|
||||||
{
|
{
|
||||||
|
expand( pSize-mSize );
|
||||||
}
|
}
|
||||||
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
|
return( mSize );
|
||||||
{
|
}
|
||||||
mHead = mStorage = new unsigned char[mAllocation];
|
//unsigned int Allocation() const { return( mAllocation ); }
|
||||||
mTail = mHead;
|
|
||||||
}
|
|
||||||
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
|
|
||||||
{
|
|
||||||
mHead = mStorage = new unsigned char[mSize];
|
|
||||||
memcpy( mStorage, pStorage, mSize );
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
|
|
||||||
{
|
|
||||||
mHead = mStorage = new unsigned char[mSize];
|
|
||||||
memcpy( mStorage, buffer.mHead, mSize );
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
~Buffer()
|
|
||||||
{
|
|
||||||
delete[] mStorage;
|
|
||||||
}
|
|
||||||
unsigned char *head() const { return( mHead ); }
|
|
||||||
unsigned char *tail() const { return( mTail ); }
|
|
||||||
unsigned int size() const { return( mSize ); }
|
|
||||||
bool empty() const { return( mSize == 0 ); }
|
|
||||||
unsigned int size( unsigned int pSize )
|
|
||||||
{
|
|
||||||
if ( mSize < pSize )
|
|
||||||
{
|
|
||||||
expand( pSize-mSize );
|
|
||||||
}
|
|
||||||
return( mSize );
|
|
||||||
}
|
|
||||||
//unsigned int Allocation() const { return( mAllocation ); }
|
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
|
{
|
||||||
|
mSize = 0;
|
||||||
|
mHead = mTail = mStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
||||||
|
unsigned int assign( const Buffer &buffer )
|
||||||
|
{
|
||||||
|
return( assign( buffer.mHead, buffer.mSize ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim from the front of the buffer
|
||||||
|
unsigned int consume( unsigned int count )
|
||||||
|
{
|
||||||
|
if ( count > mSize )
|
||||||
{
|
{
|
||||||
mSize = 0;
|
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
||||||
|
count = mSize;
|
||||||
|
}
|
||||||
|
mHead += count;
|
||||||
|
mSize -= count;
|
||||||
|
tidy( 0 );
|
||||||
|
return( count );
|
||||||
|
}
|
||||||
|
// Trim from the end of the buffer
|
||||||
|
unsigned int shrink( unsigned int count )
|
||||||
|
{
|
||||||
|
if ( count > mSize )
|
||||||
|
{
|
||||||
|
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
||||||
|
count = mSize;
|
||||||
|
}
|
||||||
|
mSize -= count;
|
||||||
|
if ( mTail > (mHead + mSize) )
|
||||||
|
mTail = mHead + mSize;
|
||||||
|
tidy( 0 );
|
||||||
|
return( count );
|
||||||
|
}
|
||||||
|
// Add to the end of the buffer
|
||||||
|
unsigned int expand( unsigned int count );
|
||||||
|
|
||||||
|
// Return pointer to the first pSize bytes and advance the head
|
||||||
|
unsigned char *extract( unsigned int pSize )
|
||||||
|
{
|
||||||
|
if ( pSize > mSize )
|
||||||
|
{
|
||||||
|
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
||||||
|
pSize = mSize;
|
||||||
|
}
|
||||||
|
unsigned char *oldHead = mHead;
|
||||||
|
mHead += pSize;
|
||||||
|
mSize -= pSize;
|
||||||
|
tidy( 0 );
|
||||||
|
return( oldHead );
|
||||||
|
}
|
||||||
|
// Add bytes to the end of the buffer
|
||||||
|
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
||||||
|
{
|
||||||
|
expand( pSize );
|
||||||
|
memcpy( mTail, pStorage, pSize );
|
||||||
|
mTail += pSize;
|
||||||
|
mSize += pSize;
|
||||||
|
return( mSize );
|
||||||
|
}
|
||||||
|
unsigned int append( const char *pStorage, unsigned int pSize )
|
||||||
|
{
|
||||||
|
return( append( (const unsigned char *)pStorage, pSize ) );
|
||||||
|
}
|
||||||
|
unsigned int append( const Buffer &buffer )
|
||||||
|
{
|
||||||
|
return( append( buffer.mHead, buffer.mSize ) );
|
||||||
|
}
|
||||||
|
void tidy( bool level=0 )
|
||||||
|
{
|
||||||
|
if ( mHead != mStorage )
|
||||||
|
{
|
||||||
|
if ( mSize == 0 )
|
||||||
mHead = mTail = mStorage;
|
mHead = mTail = mStorage;
|
||||||
}
|
else if ( level )
|
||||||
|
{
|
||||||
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
|
if ( (mHead-mStorage) > mSize )
|
||||||
unsigned int assign( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
return( assign( buffer.mHead, buffer.mSize ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim from the front of the buffer
|
|
||||||
unsigned int consume( unsigned int count )
|
|
||||||
{
|
|
||||||
if ( count > mSize )
|
|
||||||
{
|
{
|
||||||
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
|
memcpy( mStorage, mHead, mSize );
|
||||||
count = mSize;
|
mHead = mStorage;
|
||||||
|
mTail = mHead + mSize;
|
||||||
}
|
}
|
||||||
mHead += count;
|
}
|
||||||
mSize -= count;
|
|
||||||
tidy( 0 );
|
|
||||||
return( count );
|
|
||||||
}
|
}
|
||||||
// Trim from the end of the buffer
|
}
|
||||||
unsigned int shrink( unsigned int count )
|
|
||||||
{
|
|
||||||
if ( count > mSize )
|
|
||||||
{
|
|
||||||
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
|
|
||||||
count = mSize;
|
|
||||||
}
|
|
||||||
mSize -= count;
|
|
||||||
if ( mTail > (mHead + mSize) )
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
tidy( 0 );
|
|
||||||
return( count );
|
|
||||||
}
|
|
||||||
// Add to the end of the buffer
|
|
||||||
unsigned int expand( unsigned int count );
|
|
||||||
|
|
||||||
// Return pointer to the first pSize bytes and advance the head
|
Buffer &operator=( const Buffer &buffer )
|
||||||
unsigned char *extract( unsigned int pSize )
|
{
|
||||||
{
|
assign( buffer );
|
||||||
if ( pSize > mSize )
|
return( *this );
|
||||||
{
|
}
|
||||||
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
|
Buffer &operator+=( const Buffer &buffer )
|
||||||
pSize = mSize;
|
{
|
||||||
}
|
append( buffer );
|
||||||
unsigned char *oldHead = mHead;
|
return( *this );
|
||||||
mHead += pSize;
|
}
|
||||||
mSize -= pSize;
|
Buffer &operator+=( unsigned int count )
|
||||||
tidy( 0 );
|
{
|
||||||
return( oldHead );
|
expand( count );
|
||||||
}
|
return( *this );
|
||||||
// Add bytes to the end of the buffer
|
}
|
||||||
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
|
Buffer &operator-=( unsigned int count )
|
||||||
{
|
{
|
||||||
expand( pSize );
|
consume( count );
|
||||||
memcpy( mTail, pStorage, pSize );
|
return( *this );
|
||||||
mTail += pSize;
|
}
|
||||||
mSize += pSize;
|
operator unsigned char *() const
|
||||||
return( mSize );
|
{
|
||||||
}
|
return( mHead );
|
||||||
unsigned int append( const char *pStorage, unsigned int pSize )
|
}
|
||||||
{
|
operator char *() const
|
||||||
return( append( (const unsigned char *)pStorage, pSize ) );
|
{
|
||||||
}
|
return( (char *)mHead );
|
||||||
unsigned int append( const Buffer &buffer )
|
}
|
||||||
{
|
unsigned char *operator+(int offset) const
|
||||||
return( append( buffer.mHead, buffer.mSize ) );
|
{
|
||||||
}
|
return( (unsigned char *)(mHead+offset) );
|
||||||
void tidy( bool level=0 )
|
}
|
||||||
{
|
unsigned char operator[](int index) const
|
||||||
if ( mHead != mStorage )
|
{
|
||||||
{
|
return( *(mHead+index) );
|
||||||
if ( mSize == 0 )
|
}
|
||||||
mHead = mTail = mStorage;
|
operator int () const
|
||||||
else if ( level )
|
{
|
||||||
{
|
return( (int)mSize );
|
||||||
if ( (mHead-mStorage) > mSize )
|
}
|
||||||
{
|
int read_into( int sd, unsigned int bytes );
|
||||||
memcpy( mStorage, mHead, mSize );
|
|
||||||
mHead = mStorage;
|
|
||||||
mTail = mHead + mSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer &operator=( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
assign( buffer );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator+=( const Buffer &buffer )
|
|
||||||
{
|
|
||||||
append( buffer );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator+=( unsigned int count )
|
|
||||||
{
|
|
||||||
expand( count );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
Buffer &operator-=( unsigned int count )
|
|
||||||
{
|
|
||||||
consume( count );
|
|
||||||
return( *this );
|
|
||||||
}
|
|
||||||
operator unsigned char *() const
|
|
||||||
{
|
|
||||||
return( mHead );
|
|
||||||
}
|
|
||||||
operator char *() const
|
|
||||||
{
|
|
||||||
return( (char *)mHead );
|
|
||||||
}
|
|
||||||
unsigned char *operator+(int offset) const
|
|
||||||
{
|
|
||||||
return( (unsigned char *)(mHead+offset) );
|
|
||||||
}
|
|
||||||
unsigned char operator[](int index) const
|
|
||||||
{
|
|
||||||
return( *(mHead+index) );
|
|
||||||
}
|
|
||||||
operator int () const
|
|
||||||
{
|
|
||||||
return( (int)mSize );
|
|
||||||
}
|
|
||||||
int read_into( int sd, unsigned int bytes );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_BUFFER_H
|
#endif // ZM_BUFFER_H
|
||||||
|
|
|
@ -21,29 +21,29 @@
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
|
|
||||||
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
id( p_id ),
|
id( p_id ),
|
||||||
type( p_type ),
|
type( p_type ),
|
||||||
width( p_width),
|
width( p_width),
|
||||||
height( p_height ),
|
height( p_height ),
|
||||||
colours( p_colours ),
|
colours( p_colours ),
|
||||||
subpixelorder( p_subpixelorder ),
|
subpixelorder( p_subpixelorder ),
|
||||||
brightness( p_brightness ),
|
brightness( p_brightness ),
|
||||||
hue( p_hue ),
|
hue( p_hue ),
|
||||||
colour( p_colour ),
|
colour( p_colour ),
|
||||||
contrast( p_contrast ),
|
contrast( p_contrast ),
|
||||||
capture( p_capture )
|
capture( p_capture )
|
||||||
{
|
{
|
||||||
pixels = width * height;
|
pixels = width * height;
|
||||||
imagesize = pixels * colours;
|
imagesize = pixels * colours;
|
||||||
|
|
||||||
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
|
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
|
||||||
|
|
||||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
||||||
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
|
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
|
||||||
Fatal("Image size is not multiples of 16");
|
Fatal("Image size is not multiples of 16");
|
||||||
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
|
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
|
||||||
Fatal("Image size is not multiples of 12 and 16");
|
Fatal("Image size is not multiples of 12 and 16");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera::~Camera()
|
Camera::~Camera()
|
||||||
|
|
|
@ -32,52 +32,52 @@
|
||||||
class Camera
|
class Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
|
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
SourceType type;
|
SourceType type;
|
||||||
unsigned int width;
|
unsigned int width;
|
||||||
unsigned int height;
|
unsigned int height;
|
||||||
unsigned int colours;
|
unsigned int colours;
|
||||||
unsigned int subpixelorder;
|
unsigned int subpixelorder;
|
||||||
unsigned int pixels;
|
unsigned int pixels;
|
||||||
unsigned int imagesize;
|
unsigned int imagesize;
|
||||||
int brightness;
|
int brightness;
|
||||||
int hue;
|
int hue;
|
||||||
int colour;
|
int colour;
|
||||||
int contrast;
|
int contrast;
|
||||||
bool capture;
|
bool capture;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
virtual ~Camera();
|
virtual ~Camera();
|
||||||
|
|
||||||
int getId() const { return( id ); }
|
int getId() const { return( id ); }
|
||||||
SourceType Type() const { return( type ); }
|
SourceType Type() const { return( type ); }
|
||||||
bool IsLocal() const { return( type == LOCAL_SRC ); }
|
bool IsLocal() const { return( type == LOCAL_SRC ); }
|
||||||
bool IsRemote() const { return( type == REMOTE_SRC ); }
|
bool IsRemote() const { return( type == REMOTE_SRC ); }
|
||||||
bool IsFile() const { return( type == FILE_SRC ); }
|
bool IsFile() const { return( type == FILE_SRC ); }
|
||||||
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
bool IsFfmpeg() const { return( type == FFMPEG_SRC ); }
|
||||||
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
bool IsLibvlc() const { return( type == LIBVLC_SRC ); }
|
||||||
bool IscURL() const { return( type == CURL_SRC ); }
|
bool IscURL() const { return( type == CURL_SRC ); }
|
||||||
unsigned int Width() const { return( width ); }
|
unsigned int Width() const { return( width ); }
|
||||||
unsigned int Height() const { return( height ); }
|
unsigned int Height() const { return( height ); }
|
||||||
unsigned int Colours() const { return( colours ); }
|
unsigned int Colours() const { return( colours ); }
|
||||||
unsigned int SubpixelOrder() const { return( subpixelorder ); }
|
unsigned int SubpixelOrder() const { return( subpixelorder ); }
|
||||||
unsigned int Pixels() const { return( pixels ); }
|
unsigned int Pixels() const { return( pixels ); }
|
||||||
unsigned int ImageSize() const { return( imagesize ); }
|
unsigned int ImageSize() const { return( imagesize ); }
|
||||||
|
|
||||||
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
|
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
|
||||||
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
|
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
|
||||||
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
|
virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); }
|
||||||
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
|
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
|
||||||
|
|
||||||
bool CanCapture() const { return( capture ); }
|
bool CanCapture() const { return( capture ); }
|
||||||
|
|
||||||
virtual int PrimeCapture() { return( 0 ); }
|
virtual int PrimeCapture() { return( 0 ); }
|
||||||
virtual int PreCapture()=0;
|
virtual int PreCapture()=0;
|
||||||
virtual int Capture( Image &image )=0;
|
virtual int Capture( Image &image )=0;
|
||||||
virtual int PostCapture()=0;
|
virtual int PostCapture()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_CAMERA_H
|
#endif // ZM_CAMERA_H
|
||||||
|
|
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()
|
void zmLoadConfig()
|
||||||
{
|
{
|
||||||
FILE *cfg;
|
FILE *cfg;
|
||||||
char line[512];
|
char line[512];
|
||||||
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
|
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
|
||||||
{
|
{
|
||||||
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
|
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
|
||||||
}
|
}
|
||||||
while ( fgets( line, sizeof(line), cfg ) != NULL )
|
while ( fgets( line, sizeof(line), cfg ) != NULL )
|
||||||
{
|
{
|
||||||
char *line_ptr = line;
|
char *line_ptr = line;
|
||||||
|
|
||||||
// Trim off any cr/lf line endings
|
// Trim off any cr/lf line endings
|
||||||
int chomp_len = strcspn( line_ptr, "\r\n" );
|
int chomp_len = strcspn( line_ptr, "\r\n" );
|
||||||
line_ptr[chomp_len] = '\0';
|
line_ptr[chomp_len] = '\0';
|
||||||
|
|
||||||
// Remove leading white space
|
// Remove leading white space
|
||||||
int white_len = strspn( line_ptr, " \t" );
|
int white_len = strspn( line_ptr, " \t" );
|
||||||
line_ptr += white_len;
|
line_ptr += white_len;
|
||||||
|
|
||||||
// Check for comment or empty line
|
// Check for comment or empty line
|
||||||
if ( *line_ptr == '\0' || *line_ptr == '#' )
|
if ( *line_ptr == '\0' || *line_ptr == '#' )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Remove trailing white space
|
// Remove trailing white space
|
||||||
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
|
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
|
||||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
|
while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
|
||||||
{
|
{
|
||||||
*temp_ptr-- = '\0';
|
*temp_ptr-- = '\0';
|
||||||
temp_ptr--;
|
temp_ptr--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now look for the '=' in the middle of the line
|
// Now look for the '=' in the middle of the line
|
||||||
temp_ptr = strchr( line_ptr, '=' );
|
temp_ptr = strchr( line_ptr, '=' );
|
||||||
if ( !temp_ptr )
|
if ( !temp_ptr )
|
||||||
{
|
{
|
||||||
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
|
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign the name and value parts
|
// Assign the name and value parts
|
||||||
char *name_ptr = line_ptr;
|
char *name_ptr = line_ptr;
|
||||||
char *val_ptr = temp_ptr+1;
|
char *val_ptr = temp_ptr+1;
|
||||||
|
|
||||||
// Trim trailing space from the name part
|
// Trim trailing space from the name part
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
*temp_ptr = '\0';
|
*temp_ptr = '\0';
|
||||||
temp_ptr--;
|
temp_ptr--;
|
||||||
}
|
}
|
||||||
while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
|
while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
|
||||||
|
|
||||||
// Remove leading white space from the value part
|
// Remove leading white space from the value part
|
||||||
white_len = strspn( val_ptr, " \t" );
|
white_len = strspn( val_ptr, " \t" );
|
||||||
val_ptr += white_len;
|
val_ptr += white_len;
|
||||||
|
|
||||||
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
|
if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )
|
||||||
staticConfig.DB_HOST = std::string(val_ptr);
|
staticConfig.DB_HOST = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )
|
||||||
staticConfig.DB_NAME = std::string(val_ptr);
|
staticConfig.DB_NAME = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )
|
||||||
staticConfig.DB_USER = std::string(val_ptr);
|
staticConfig.DB_USER = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||||
staticConfig.DB_PASS = std::string(val_ptr);
|
staticConfig.DB_PASS = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 )
|
||||||
staticConfig.SERVER_NAME = std::string(val_ptr);
|
staticConfig.SERVER_NAME = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
|
||||||
staticConfig.SERVER_ID = atoi(val_ptr);
|
staticConfig.SERVER_ID = atoi(val_ptr);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// We ignore this now as there may be more parameters than the
|
// We ignore this now as there may be more parameters than the
|
||||||
// c/c++ binaries are bothered about
|
// c/c++ binaries are bothered about
|
||||||
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
|
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
|
||||||
}
|
}
|
||||||
} // end foreach line of the config
|
} // end foreach line of the config
|
||||||
fclose( cfg );
|
fclose( cfg );
|
||||||
zmDbConnect();
|
zmDbConnect();
|
||||||
config.Load();
|
config.Load();
|
||||||
config.Assign();
|
config.Assign();
|
||||||
|
|
||||||
// Populate the server config entries
|
// Populate the server config entries
|
||||||
if ( ! staticConfig.SERVER_ID ) {
|
if ( ! staticConfig.SERVER_ID ) {
|
||||||
if ( ! staticConfig.SERVER_NAME.empty() ) {
|
if ( ! staticConfig.SERVER_NAME.empty() ) {
|
||||||
|
|
||||||
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
|
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
|
||||||
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
|
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
|
||||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||||
staticConfig.SERVER_ID = atoi(dbrow[0]);
|
staticConfig.SERVER_ID = atoi(dbrow[0]);
|
||||||
} else {
|
} else {
|
||||||
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
|
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end if has SERVER_NAME
|
} // end if has SERVER_NAME
|
||||||
} else if ( staticConfig.SERVER_NAME.empty() ) {
|
} else if ( staticConfig.SERVER_NAME.empty() ) {
|
||||||
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
|
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
|
||||||
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
|
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
|
||||||
|
|
||||||
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) {
|
||||||
staticConfig.SERVER_NAME = std::string(dbrow[0]);
|
staticConfig.SERVER_NAME = std::string(dbrow[0]);
|
||||||
} else {
|
} else {
|
||||||
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
|
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if ( ! staticConfig.SERVER_ID ) {
|
if ( ! staticConfig.SERVER_ID ) {
|
||||||
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." );
|
Debug( 1, "No Server ID or Name specified in config. Not using Multi-Server Mode." );
|
||||||
} else {
|
} else {
|
||||||
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
|
Debug( 1, "Server is %d: using Multi-Server Mode.", staticConfig.SERVER_ID );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticConfig staticConfig;
|
StaticConfig staticConfig;
|
||||||
|
|
||||||
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type )
|
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type )
|
||||||
{
|
{
|
||||||
name = new char[strlen(p_name)+1];
|
name = new char[strlen(p_name)+1];
|
||||||
strcpy( name, p_name );
|
strcpy( name, p_name );
|
||||||
value = new char[strlen(p_value)+1];
|
value = new char[strlen(p_value)+1];
|
||||||
strcpy( value, p_value );
|
strcpy( value, p_value );
|
||||||
type = new char[strlen(p_type)+1];
|
type = new char[strlen(p_type)+1];
|
||||||
strcpy( type, p_type );
|
strcpy( type, p_type );
|
||||||
|
|
||||||
//Info( "Created new config item %s = %s (%s)\n", name, value, type );
|
//Info( "Created new config item %s = %s (%s)\n", name, value, type );
|
||||||
|
|
||||||
accessed = false;
|
accessed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigItem::~ConfigItem()
|
ConfigItem::~ConfigItem()
|
||||||
{
|
{
|
||||||
delete[] name;
|
delete[] name;
|
||||||
delete[] value;
|
delete[] value;
|
||||||
delete[] type;
|
delete[] type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigItem::ConvertValue() const
|
void ConfigItem::ConvertValue() const
|
||||||
{
|
{
|
||||||
if ( !strcmp( type, "boolean" ) )
|
if ( !strcmp( type, "boolean" ) )
|
||||||
{
|
{
|
||||||
cfg_type = CFG_BOOLEAN;
|
cfg_type = CFG_BOOLEAN;
|
||||||
cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
|
cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
|
||||||
}
|
}
|
||||||
else if ( !strcmp( type, "integer" ) )
|
else if ( !strcmp( type, "integer" ) )
|
||||||
{
|
{
|
||||||
cfg_type = CFG_INTEGER;
|
cfg_type = CFG_INTEGER;
|
||||||
cfg_value.integer_value = strtol( value, 0, 10 );
|
cfg_value.integer_value = strtol( value, 0, 10 );
|
||||||
}
|
}
|
||||||
else if ( !strcmp( type, "hexadecimal" ) )
|
else if ( !strcmp( type, "hexadecimal" ) )
|
||||||
{
|
{
|
||||||
cfg_type = CFG_INTEGER;
|
cfg_type = CFG_INTEGER;
|
||||||
cfg_value.integer_value = strtol( value, 0, 16 );
|
cfg_value.integer_value = strtol( value, 0, 16 );
|
||||||
}
|
}
|
||||||
else if ( !strcmp( type, "decimal" ) )
|
else if ( !strcmp( type, "decimal" ) )
|
||||||
{
|
{
|
||||||
cfg_type = CFG_DECIMAL;
|
cfg_type = CFG_DECIMAL;
|
||||||
cfg_value.decimal_value = strtod( value, 0 );
|
cfg_value.decimal_value = strtod( value, 0 );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cfg_type = CFG_STRING;
|
cfg_type = CFG_STRING;
|
||||||
cfg_value.string_value = value;
|
cfg_value.string_value = value;
|
||||||
}
|
}
|
||||||
accessed = true;
|
accessed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigItem::BooleanValue() const
|
bool ConfigItem::BooleanValue() const
|
||||||
{
|
{
|
||||||
if ( !accessed )
|
if ( !accessed )
|
||||||
ConvertValue();
|
ConvertValue();
|
||||||
|
|
||||||
if ( cfg_type != CFG_BOOLEAN )
|
if ( cfg_type != CFG_BOOLEAN )
|
||||||
{
|
{
|
||||||
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( cfg_value.boolean_value );
|
return( cfg_value.boolean_value );
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConfigItem::IntegerValue() const
|
int ConfigItem::IntegerValue() const
|
||||||
{
|
{
|
||||||
if ( !accessed )
|
if ( !accessed )
|
||||||
ConvertValue();
|
ConvertValue();
|
||||||
|
|
||||||
if ( cfg_type != CFG_INTEGER )
|
if ( cfg_type != CFG_INTEGER )
|
||||||
{
|
{
|
||||||
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( cfg_value.integer_value );
|
return( cfg_value.integer_value );
|
||||||
}
|
}
|
||||||
|
|
||||||
double ConfigItem::DecimalValue() const
|
double ConfigItem::DecimalValue() const
|
||||||
{
|
{
|
||||||
if ( !accessed )
|
if ( !accessed )
|
||||||
ConvertValue();
|
ConvertValue();
|
||||||
|
|
||||||
if ( cfg_type != CFG_DECIMAL )
|
if ( cfg_type != CFG_DECIMAL )
|
||||||
{
|
{
|
||||||
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( cfg_value.decimal_value );
|
return( cfg_value.decimal_value );
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *ConfigItem::StringValue() const
|
const char *ConfigItem::StringValue() const
|
||||||
{
|
{
|
||||||
if ( !accessed )
|
if ( !accessed )
|
||||||
ConvertValue();
|
ConvertValue();
|
||||||
|
|
||||||
if ( cfg_type != CFG_STRING )
|
if ( cfg_type != CFG_STRING )
|
||||||
{
|
{
|
||||||
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( cfg_value.string_value );
|
return( cfg_value.string_value );
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::Config()
|
Config::Config()
|
||||||
{
|
{
|
||||||
n_items = 0;
|
n_items = 0;
|
||||||
items = 0;
|
items = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config()
|
Config::~Config()
|
||||||
{
|
{
|
||||||
if ( items )
|
if ( items )
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < n_items; i++ )
|
for ( int i = 0; i < n_items; i++ )
|
||||||
{
|
{
|
||||||
delete items[i];
|
delete items[i];
|
||||||
}
|
}
|
||||||
delete[] items;
|
delete[] items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Load()
|
void Config::Load()
|
||||||
{
|
{
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
|
|
||||||
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
|
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result )
|
if ( !result )
|
||||||
{
|
{
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
n_items = mysql_num_rows( result );
|
n_items = mysql_num_rows( result );
|
||||||
|
|
||||||
if ( n_items <= ZM_MAX_CFG_ID )
|
if ( n_items <= ZM_MAX_CFG_ID )
|
||||||
{
|
{
|
||||||
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
|
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
items = new ConfigItem *[n_items];
|
items = new ConfigItem *[n_items];
|
||||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
|
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
|
||||||
{
|
{
|
||||||
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
|
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
|
||||||
}
|
}
|
||||||
mysql_free_result( result );
|
mysql_free_result( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::Assign()
|
void Config::Assign()
|
||||||
|
@ -309,27 +309,27 @@ ZM_CFG_ASSIGN_LIST
|
||||||
|
|
||||||
const ConfigItem &Config::Item( int id )
|
const ConfigItem &Config::Item( int id )
|
||||||
{
|
{
|
||||||
if ( !n_items )
|
if ( !n_items )
|
||||||
{
|
{
|
||||||
Load();
|
Load();
|
||||||
Assign();
|
Assign();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( id < 0 || id > ZM_MAX_CFG_ID )
|
if ( id < 0 || id > ZM_MAX_CFG_ID )
|
||||||
{
|
{
|
||||||
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
|
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigItem *item = items[id];
|
ConfigItem *item = items[id];
|
||||||
|
|
||||||
if ( !item )
|
if ( !item )
|
||||||
{
|
{
|
||||||
Error( "Can't find config item %d", id );
|
Error( "Can't find config item %d", id );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( *item );
|
return( *item );
|
||||||
}
|
}
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
|
|
@ -25,48 +25,48 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file
|
||||||
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
#define ZM_VERSION "@VERSION@" // ZoneMinder Version
|
||||||
|
|
||||||
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
#define ZM_HAS_V4L1 @ZM_HAS_V4L1@
|
||||||
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
#define ZM_HAS_V4L2 @ZM_HAS_V4L2@
|
||||||
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
#define ZM_HAS_V4L @ZM_HAS_V4L@
|
||||||
|
|
||||||
#ifdef HAVE_LIBAVFORMAT
|
#ifdef HAVE_LIBAVFORMAT
|
||||||
#define ZM_HAS_FFMPEG 1
|
#define ZM_HAS_FFMPEG 1
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
|
||||||
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT)
|
||||||
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS)
|
||||||
|
|
||||||
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
|
||||||
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
|
||||||
|
|
||||||
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
|
||||||
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
|
||||||
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
|
||||||
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
|
||||||
|
|
||||||
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
|
||||||
|
|
||||||
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
|
||||||
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc
|
||||||
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc
|
||||||
|
|
||||||
extern void zmLoadConfig();
|
extern void zmLoadConfig();
|
||||||
|
|
||||||
struct StaticConfig
|
struct StaticConfig
|
||||||
{
|
{
|
||||||
std::string DB_HOST;
|
std::string DB_HOST;
|
||||||
std::string DB_NAME;
|
std::string DB_NAME;
|
||||||
std::string DB_USER;
|
std::string DB_USER;
|
||||||
std::string DB_PASS;
|
std::string DB_PASS;
|
||||||
std::string PATH_WEB;
|
std::string PATH_WEB;
|
||||||
std::string SERVER_NAME;
|
std::string SERVER_NAME;
|
||||||
unsigned int SERVER_ID;
|
unsigned int SERVER_ID;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern StaticConfig staticConfig;
|
extern StaticConfig staticConfig;
|
||||||
|
@ -74,63 +74,63 @@ extern StaticConfig staticConfig;
|
||||||
class ConfigItem
|
class ConfigItem
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
char *name;
|
char *name;
|
||||||
char *value;
|
char *value;
|
||||||
char *type;
|
char *type;
|
||||||
|
|
||||||
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
|
||||||
mutable union
|
mutable union
|
||||||
{
|
{
|
||||||
bool boolean_value;
|
bool boolean_value;
|
||||||
int integer_value;
|
int integer_value;
|
||||||
double decimal_value;
|
double decimal_value;
|
||||||
char *string_value;
|
char *string_value;
|
||||||
} cfg_value;
|
} cfg_value;
|
||||||
mutable bool accessed;
|
mutable bool accessed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
ConfigItem( const char *p_name, const char *p_value, const char *const p_type );
|
||||||
~ConfigItem();
|
~ConfigItem();
|
||||||
void ConvertValue() const;
|
void ConvertValue() const;
|
||||||
bool BooleanValue() const;
|
bool BooleanValue() const;
|
||||||
int IntegerValue() const;
|
int IntegerValue() const;
|
||||||
double DecimalValue() const;
|
double DecimalValue() const;
|
||||||
const char *StringValue() const;
|
const char *StringValue() const;
|
||||||
|
|
||||||
inline operator bool() const
|
inline operator bool() const
|
||||||
{
|
{
|
||||||
return( BooleanValue() );
|
return( BooleanValue() );
|
||||||
}
|
}
|
||||||
inline operator int() const
|
inline operator int() const
|
||||||
{
|
{
|
||||||
return( IntegerValue() );
|
return( IntegerValue() );
|
||||||
}
|
}
|
||||||
inline operator double() const
|
inline operator double() const
|
||||||
{
|
{
|
||||||
return( DecimalValue() );
|
return( DecimalValue() );
|
||||||
}
|
}
|
||||||
inline operator const char *() const
|
inline operator const char *() const
|
||||||
{
|
{
|
||||||
return( StringValue() );
|
return( StringValue() );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ZM_CFG_DECLARE_LIST
|
ZM_CFG_DECLARE_LIST
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int n_items;
|
int n_items;
|
||||||
ConfigItem **items;
|
ConfigItem **items;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
|
|
||||||
void Load();
|
void Load();
|
||||||
void Assign();
|
void Assign();
|
||||||
const ConfigItem &Item( int id );
|
const ConfigItem &Item( int id );
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Config config;
|
extern Config config;
|
||||||
|
|
|
@ -28,40 +28,40 @@
|
||||||
class Coord
|
class Coord
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Coord() : x(0), y(0)
|
inline Coord() : x(0), y(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
inline Coord( int p_x, int p_y ) : x(p_x), y(p_y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
inline int &X() { return( x ); }
|
inline int &X() { return( x ); }
|
||||||
inline const int &X() const { return( x ); }
|
inline const int &X() const { return( x ); }
|
||||||
inline int &Y() { return( y ); }
|
inline int &Y() { return( y ); }
|
||||||
inline const int &Y() const { return( y ); }
|
inline const int &Y() const { return( y ); }
|
||||||
|
|
||||||
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
inline static Coord Range( const Coord &coord1, const Coord &coord2 )
|
||||||
{
|
{
|
||||||
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); }
|
||||||
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); }
|
||||||
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); }
|
||||||
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); }
|
||||||
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); }
|
||||||
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); }
|
||||||
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); }
|
||||||
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); }
|
||||||
|
|
||||||
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); }
|
||||||
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_COORD_H
|
#endif // ZM_COORD_H
|
||||||
|
|
|
@ -31,523 +31,523 @@ size_t content_length_match_len;
|
||||||
size_t content_type_match_len;
|
size_t content_type_match_len;
|
||||||
|
|
||||||
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||||
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
||||||
{
|
{
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cURLCamera::~cURLCamera()
|
cURLCamera::~cURLCamera()
|
||||||
{
|
{
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
|
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cURLCamera::Initialise()
|
void cURLCamera::Initialise()
|
||||||
{
|
{
|
||||||
content_length_match_len = strlen(content_length_match);
|
content_length_match_len = strlen(content_length_match);
|
||||||
content_type_match_len = strlen(content_type_match);
|
content_type_match_len = strlen(content_type_match);
|
||||||
|
|
||||||
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
|
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
|
||||||
|
|
||||||
/* cURL initialization */
|
/* cURL initialization */
|
||||||
cRet = curl_global_init(CURL_GLOBAL_ALL);
|
cRet = curl_global_init(CURL_GLOBAL_ALL);
|
||||||
if(cRet != CURLE_OK) {
|
if(cRet != CURLE_OK) {
|
||||||
Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet));
|
Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet));
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(2,"libcurl version: %s",curl_version());
|
Debug(2,"libcurl version: %s",curl_version());
|
||||||
|
|
||||||
/* Create the shared data mutex */
|
/* Create the shared data mutex */
|
||||||
nRet = pthread_mutex_init(&shareddata_mutex, NULL);
|
nRet = pthread_mutex_init(&shareddata_mutex, NULL);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Fatal("Shared data mutex creation failed: %s",strerror(nRet));
|
Fatal("Shared data mutex creation failed: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
/* Create the data available condition variable */
|
/* Create the data available condition variable */
|
||||||
nRet = pthread_cond_init(&data_available_cond, NULL);
|
nRet = pthread_cond_init(&data_available_cond, NULL);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Fatal("Data available condition variable creation failed: %s",strerror(nRet));
|
Fatal("Data available condition variable creation failed: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
/* Create the request complete condition variable */
|
/* Create the request complete condition variable */
|
||||||
nRet = pthread_cond_init(&request_complete_cond, NULL);
|
nRet = pthread_cond_init(&request_complete_cond, NULL);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Fatal("Request complete condition variable creation failed: %s",strerror(nRet));
|
Fatal("Request complete condition variable creation failed: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the thread */
|
/* Create the thread */
|
||||||
nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this);
|
nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Fatal("Thread creation failed: %s",strerror(nRet));
|
Fatal("Thread creation failed: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cURLCamera::Terminate()
|
void cURLCamera::Terminate()
|
||||||
{
|
{
|
||||||
/* Signal the thread to terminate */
|
/* Signal the thread to terminate */
|
||||||
bTerminate = true;
|
bTerminate = true;
|
||||||
|
|
||||||
/* Wait for thread termination */
|
/* Wait for thread termination */
|
||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
|
|
||||||
/* Destroy condition variables */
|
/* Destroy condition variables */
|
||||||
pthread_cond_destroy(&request_complete_cond);
|
pthread_cond_destroy(&request_complete_cond);
|
||||||
pthread_cond_destroy(&data_available_cond);
|
pthread_cond_destroy(&data_available_cond);
|
||||||
|
|
||||||
/* Destroy mutex */
|
/* Destroy mutex */
|
||||||
pthread_mutex_destroy(&shareddata_mutex);
|
pthread_mutex_destroy(&shareddata_mutex);
|
||||||
|
|
||||||
/* cURL cleanup */
|
/* cURL cleanup */
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::PrimeCapture()
|
int cURLCamera::PrimeCapture()
|
||||||
{
|
{
|
||||||
//Info( "Priming capture from %s", mPath.c_str() );
|
//Info( "Priming capture from %s", mPath.c_str() );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::PreCapture()
|
int cURLCamera::PreCapture()
|
||||||
{
|
{
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::Capture( Image &image )
|
int cURLCamera::Capture( Image &image )
|
||||||
{
|
{
|
||||||
bool frameComplete = false;
|
bool frameComplete = false;
|
||||||
|
|
||||||
/* MODE_STREAM specific variables */
|
/* MODE_STREAM specific variables */
|
||||||
bool SubHeadersParsingComplete = false;
|
bool SubHeadersParsingComplete = false;
|
||||||
unsigned int frame_content_length = 0;
|
unsigned int frame_content_length = 0;
|
||||||
std::string frame_content_type;
|
std::string frame_content_type;
|
||||||
bool need_more_data = false;
|
bool need_more_data = false;
|
||||||
|
|
||||||
/* Grab the mutex to ensure exclusive access to the shared data */
|
/* Grab the mutex to ensure exclusive access to the shared data */
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
while (!frameComplete) {
|
while (!frameComplete) {
|
||||||
|
|
||||||
/* If the work thread did a reset, reset our local variables */
|
/* If the work thread did a reset, reset our local variables */
|
||||||
if(bReset) {
|
if(bReset) {
|
||||||
SubHeadersParsingComplete = false;
|
SubHeadersParsingComplete = false;
|
||||||
frame_content_length = 0;
|
frame_content_length = 0;
|
||||||
frame_content_type.clear();
|
frame_content_type.clear();
|
||||||
need_more_data = false;
|
need_more_data = false;
|
||||||
bReset = false;
|
bReset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode == MODE_UNSET) {
|
if(mode == MODE_UNSET) {
|
||||||
/* Don't have a mode yet. Sleep while waiting for data */
|
/* Don't have a mode yet. Sleep while waiting for data */
|
||||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||||
return -20;
|
return -20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode == MODE_STREAM) {
|
if(mode == MODE_STREAM) {
|
||||||
|
|
||||||
/* Subheader parsing */
|
/* Subheader parsing */
|
||||||
while(!SubHeadersParsingComplete && !need_more_data) {
|
while(!SubHeadersParsingComplete && !need_more_data) {
|
||||||
|
|
||||||
size_t crlf_start, crlf_end, crlf_size;
|
size_t crlf_start, crlf_end, crlf_size;
|
||||||
std::string subheader;
|
std::string subheader;
|
||||||
|
|
||||||
/* Check if the buffer contains something */
|
/* Check if the buffer contains something */
|
||||||
if(databuffer.empty()) {
|
if(databuffer.empty()) {
|
||||||
/* Empty buffer, wait for data */
|
/* Empty buffer, wait for data */
|
||||||
need_more_data = true;
|
need_more_data = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find crlf start */
|
/* Find crlf start */
|
||||||
crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
|
crlf_start = memcspn(databuffer,"\r\n",databuffer.size());
|
||||||
if(crlf_start == databuffer.size()) {
|
if(crlf_start == databuffer.size()) {
|
||||||
/* Not found, wait for more data */
|
/* Not found, wait for more data */
|
||||||
need_more_data = true;
|
need_more_data = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if we have enough data for determining crlf length */
|
/* See if we have enough data for determining crlf length */
|
||||||
if(databuffer.size() < crlf_start+5) {
|
if(databuffer.size() < crlf_start+5) {
|
||||||
/* Need more data */
|
/* Need more data */
|
||||||
need_more_data = true;
|
need_more_data = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find crlf end and calculate crlf size */
|
/* Find crlf end and calculate crlf size */
|
||||||
crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
|
crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5);
|
||||||
crlf_size = (crlf_start + crlf_end) - crlf_start;
|
crlf_size = (crlf_start + crlf_end) - crlf_start;
|
||||||
|
|
||||||
/* Is this the end of a previous stream? (This is just before the boundary) */
|
/* Is this the end of a previous stream? (This is just before the boundary) */
|
||||||
if(crlf_start == 0) {
|
if(crlf_start == 0) {
|
||||||
databuffer.consume(crlf_size);
|
databuffer.consume(crlf_size);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for invalid CRLF size */
|
/* Check for invalid CRLF size */
|
||||||
if(crlf_size > 4) {
|
if(crlf_size > 4) {
|
||||||
Error("Invalid CRLF length");
|
Error("Invalid CRLF length");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
|
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
|
||||||
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
|
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
|
||||||
/* This is the last header */
|
/* This is the last header */
|
||||||
SubHeadersParsingComplete = true;
|
SubHeadersParsingComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the subheader, excluding the crlf */
|
/* Copy the subheader, excluding the crlf */
|
||||||
subheader.assign(databuffer, crlf_start);
|
subheader.assign(databuffer, crlf_start);
|
||||||
|
|
||||||
/* Advance the buffer past this one */
|
/* Advance the buffer past this one */
|
||||||
databuffer.consume(crlf_start+crlf_size);
|
databuffer.consume(crlf_start+crlf_size);
|
||||||
|
|
||||||
Debug(7,"Got subheader: %s",subheader.c_str());
|
Debug(7,"Got subheader: %s",subheader.c_str());
|
||||||
|
|
||||||
/* Find where the data in this header starts */
|
/* Find where the data in this header starts */
|
||||||
size_t subheader_data_start = subheader.rfind(' ');
|
size_t subheader_data_start = subheader.rfind(' ');
|
||||||
if(subheader_data_start == std::string::npos) {
|
if(subheader_data_start == std::string::npos) {
|
||||||
subheader_data_start = subheader.find(':');
|
subheader_data_start = subheader.find(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract the data into a string */
|
/* Extract the data into a string */
|
||||||
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
|
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
|
||||||
|
|
||||||
Debug(8,"Got subheader data: %s",subheader_data.c_str());
|
Debug(8,"Got subheader data: %s",subheader_data.c_str());
|
||||||
|
|
||||||
/* Check the header */
|
/* Check the header */
|
||||||
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
|
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
|
||||||
/* Found the content-length header */
|
/* Found the content-length header */
|
||||||
frame_content_length = atoi(subheader_data.c_str());
|
frame_content_length = atoi(subheader_data.c_str());
|
||||||
Debug(6,"Got content-length subheader: %d",frame_content_length);
|
Debug(6,"Got content-length subheader: %d",frame_content_length);
|
||||||
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
|
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||||
/* Found the content-type header */
|
/* Found the content-type header */
|
||||||
frame_content_type = subheader_data;
|
frame_content_type = subheader_data;
|
||||||
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
|
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to extract the frame */
|
/* Attempt to extract the frame */
|
||||||
if(!need_more_data) {
|
if(!need_more_data) {
|
||||||
if(!SubHeadersParsingComplete) {
|
if(!SubHeadersParsingComplete) {
|
||||||
/* We haven't parsed all headers yet */
|
/* We haven't parsed all headers yet */
|
||||||
need_more_data = true;
|
need_more_data = true;
|
||||||
} else if(frame_content_length <= 0) {
|
} else if(frame_content_length <= 0) {
|
||||||
/* Invalid frame */
|
/* Invalid frame */
|
||||||
Error("Invalid frame: invalid content length");
|
Error("Invalid frame: invalid content length");
|
||||||
} else if(frame_content_type != "image/jpeg") {
|
} else if(frame_content_type != "image/jpeg") {
|
||||||
/* Unsupported frame type */
|
/* Unsupported frame type */
|
||||||
Error("Unsupported frame: %s",frame_content_type.c_str());
|
Error("Unsupported frame: %s",frame_content_type.c_str());
|
||||||
} else if(frame_content_length > databuffer.size()) {
|
} else if(frame_content_length > databuffer.size()) {
|
||||||
/* Incomplete frame, wait for more data */
|
/* Incomplete frame, wait for more data */
|
||||||
need_more_data = true;
|
need_more_data = true;
|
||||||
} else {
|
} else {
|
||||||
/* All good. decode the image */
|
/* All good. decode the image */
|
||||||
image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
|
image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
|
||||||
frameComplete = true;
|
frameComplete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to get more data */
|
/* Attempt to get more data */
|
||||||
if(need_more_data) {
|
if(need_more_data) {
|
||||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||||
return -18;
|
return -18;
|
||||||
}
|
}
|
||||||
need_more_data = false;
|
need_more_data = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(mode == MODE_SINGLE) {
|
} else if(mode == MODE_SINGLE) {
|
||||||
/* Check if we have anything */
|
/* Check if we have anything */
|
||||||
if (!single_offsets.empty()) {
|
if (!single_offsets.empty()) {
|
||||||
if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
|
if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) {
|
||||||
/* Extract frame */
|
/* Extract frame */
|
||||||
image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
|
image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
|
||||||
single_offsets.pop_front();
|
single_offsets.pop_front();
|
||||||
frameComplete = true;
|
frameComplete = true;
|
||||||
} else {
|
} else {
|
||||||
/* This shouldn't happen */
|
/* This shouldn't happen */
|
||||||
Error("Internal error. Attempting recovery");
|
Error("Internal error. Attempting recovery");
|
||||||
databuffer.consume(single_offsets.front());
|
databuffer.consume(single_offsets.front());
|
||||||
single_offsets.pop_front();
|
single_offsets.pop_front();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Don't have a frame yet, wait for the request complete condition variable */
|
/* Don't have a frame yet, wait for the request complete condition variable */
|
||||||
nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
|
nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
|
Error("Failed waiting for request complete condition variable: %s",strerror(nRet));
|
||||||
return -19;
|
return -19;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Failed to match content-type */
|
/* Failed to match content-type */
|
||||||
Fatal("Unable to match Content-Type. Check URL, username and password");
|
Fatal("Unable to match Content-Type. Check URL, username and password");
|
||||||
} /* mode */
|
} /* mode */
|
||||||
|
|
||||||
} /* frameComplete loop */
|
} /* frameComplete loop */
|
||||||
|
|
||||||
/* Release the mutex */
|
/* Release the mutex */
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
if(!frameComplete)
|
if(!frameComplete)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::PostCapture()
|
int cURLCamera::PostCapture()
|
||||||
{
|
{
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||||
{
|
{
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
/* Append the data we just received to our buffer */
|
/* Append the data we just received to our buffer */
|
||||||
databuffer.append((const char*)buffer, size*nmemb);
|
databuffer.append((const char*)buffer, size*nmemb);
|
||||||
|
|
||||||
/* Signal data available */
|
/* Signal data available */
|
||||||
nRet = pthread_cond_signal(&data_available_cond);
|
nRet = pthread_cond_signal(&data_available_cond);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed signaling data available condition variable: %s",strerror(nRet));
|
Error("Failed signaling data available condition variable: %s",strerror(nRet));
|
||||||
return -16;
|
return -16;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
/* Return bytes processed */
|
/* Return bytes processed */
|
||||||
return size*nmemb;
|
return size*nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata)
|
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||||
{
|
{
|
||||||
std::string header;
|
std::string header;
|
||||||
header.assign((const char*)buffer, size*nmemb);
|
header.assign((const char*)buffer, size*nmemb);
|
||||||
|
|
||||||
Debug(4,"Got header: %s",header.c_str());
|
Debug(4,"Got header: %s",header.c_str());
|
||||||
|
|
||||||
/* Check Content-Type header */
|
/* Check Content-Type header */
|
||||||
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||||
size_t pos = header.find(';');
|
size_t pos = header.find(';');
|
||||||
if(pos != std::string::npos) {
|
if(pos != std::string::npos) {
|
||||||
header.erase(pos, std::string::npos);
|
header.erase(pos, std::string::npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = header.rfind(' ');
|
pos = header.rfind(' ');
|
||||||
if(pos == std::string::npos) {
|
if(pos == std::string::npos) {
|
||||||
pos = header.find(':');
|
pos = header.find(':');
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string content_type = header.substr(pos+1, std::string::npos);
|
std::string content_type = header.substr(pos+1, std::string::npos);
|
||||||
Debug(6,"Content-Type is: %s",content_type.c_str());
|
Debug(6,"Content-Type is: %s",content_type.c_str());
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
const char* multipart_match = "multipart/x-mixed-replace";
|
const char* multipart_match = "multipart/x-mixed-replace";
|
||||||
const char* image_jpeg_match = "image/jpeg";
|
const char* image_jpeg_match = "image/jpeg";
|
||||||
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
||||||
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
||||||
mode = MODE_STREAM;
|
mode = MODE_STREAM;
|
||||||
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
||||||
Debug(7,"Content type matched as image/jpeg");
|
Debug(7,"Content type matched as image/jpeg");
|
||||||
mode = MODE_SINGLE;
|
mode = MODE_SINGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return bytes processed */
|
/* Return bytes processed */
|
||||||
return size*nmemb;
|
return size*nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* cURLCamera::thread_func()
|
void* cURLCamera::thread_func()
|
||||||
{
|
{
|
||||||
long tRet;
|
long tRet;
|
||||||
double dSize;
|
double dSize;
|
||||||
|
|
||||||
c = curl_easy_init();
|
c = curl_easy_init();
|
||||||
if(c == NULL) {
|
if(c == NULL) {
|
||||||
Fatal("Failed getting easy handle from libcurl");
|
Fatal("Failed getting easy handle from libcurl");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set URL */
|
/* Set URL */
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str());
|
cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str());
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet));
|
||||||
|
|
||||||
/* Header callback */
|
/* Header callback */
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
|
cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet));
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this);
|
cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet));
|
||||||
|
|
||||||
/* Data callback */
|
/* Data callback */
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
|
cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet));
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this);
|
cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet));
|
||||||
|
|
||||||
/* Progress callback */
|
/* Progress callback */
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
|
cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
|
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet));
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this);
|
cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet));
|
Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet));
|
||||||
|
|
||||||
/* Set username and password */
|
/* Set username and password */
|
||||||
if(!mUser.empty()) {
|
if(!mUser.empty()) {
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str());
|
cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str());
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Error("Failed setting username: %s", curl_easy_strerror(cRet));
|
Error("Failed setting username: %s", curl_easy_strerror(cRet));
|
||||||
}
|
}
|
||||||
if(!mPass.empty()) {
|
if(!mPass.empty()) {
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str());
|
cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str());
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Error("Failed setting password: %s", curl_easy_strerror(cRet));
|
Error("Failed setting password: %s", curl_easy_strerror(cRet));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Authenication preference */
|
/* Authenication preference */
|
||||||
cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
||||||
if(cRet != CURLE_OK)
|
if(cRet != CURLE_OK)
|
||||||
Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet));
|
Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet));
|
||||||
|
|
||||||
|
|
||||||
/* Work loop */
|
/* Work loop */
|
||||||
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
||||||
tRet = 0;
|
tRet = 0;
|
||||||
while(!bTerminate) {
|
while(!bTerminate) {
|
||||||
/* Do the work */
|
/* Do the work */
|
||||||
cRet = curl_easy_perform(c);
|
cRet = curl_easy_perform(c);
|
||||||
|
|
||||||
if(mode == MODE_SINGLE) {
|
if(mode == MODE_SINGLE) {
|
||||||
if(cRet != CURLE_OK) {
|
if(cRet != CURLE_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Attempt to get the size of the file */
|
/* Attempt to get the size of the file */
|
||||||
cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
|
cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
|
||||||
if(cRet != CURLE_OK) {
|
if(cRet != CURLE_OK) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* We need to lock for the offsets array and the condition variable */
|
/* We need to lock for the offsets array and the condition variable */
|
||||||
lock();
|
lock();
|
||||||
/* Push the size into our offsets array */
|
/* Push the size into our offsets array */
|
||||||
if(dSize > 0) {
|
if(dSize > 0) {
|
||||||
single_offsets.push_back(dSize);
|
single_offsets.push_back(dSize);
|
||||||
} else {
|
} else {
|
||||||
Fatal("Unable to get the size of the image");
|
Fatal("Unable to get the size of the image");
|
||||||
}
|
}
|
||||||
/* Signal the request complete condition variable */
|
/* Signal the request complete condition variable */
|
||||||
tRet = pthread_cond_signal(&request_complete_cond);
|
tRet = pthread_cond_signal(&request_complete_cond);
|
||||||
if(tRet != 0) {
|
if(tRet != 0) {
|
||||||
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
|
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
|
||||||
}
|
}
|
||||||
/* Unlock */
|
/* Unlock */
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
} else if (mode == MODE_STREAM) {
|
} else if (mode == MODE_STREAM) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return value checking */
|
/* Return value checking */
|
||||||
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
||||||
/* Aborted */
|
/* Aborted */
|
||||||
break;
|
break;
|
||||||
} else if (cRet != CURLE_OK) {
|
} else if (cRet != CURLE_OK) {
|
||||||
/* Some error */
|
/* Some error */
|
||||||
Error("cURL Request failed: %s",curl_easy_strerror(cRet));
|
Error("cURL Request failed: %s",curl_easy_strerror(cRet));
|
||||||
if(attempt < CURL_MAXRETRY) {
|
if(attempt < CURL_MAXRETRY) {
|
||||||
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
|
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
|
||||||
/* Do a reset */
|
/* Do a reset */
|
||||||
lock();
|
lock();
|
||||||
databuffer.clear();
|
databuffer.clear();
|
||||||
single_offsets.clear();
|
single_offsets.clear();
|
||||||
mode = MODE_UNSET;
|
mode = MODE_UNSET;
|
||||||
bReset = true;
|
bReset = true;
|
||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
tRet = -50;
|
tRet = -50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
curl_easy_cleanup(c);
|
curl_easy_cleanup(c);
|
||||||
c = NULL;
|
c = NULL;
|
||||||
|
|
||||||
return (void*)tRet;
|
return (void*)tRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::lock() {
|
int cURLCamera::lock() {
|
||||||
int nRet;
|
int nRet;
|
||||||
|
|
||||||
/* Lock shared data */
|
/* Lock shared data */
|
||||||
nRet = pthread_mutex_lock(&shareddata_mutex);
|
nRet = pthread_mutex_lock(&shareddata_mutex);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed locking shared data mutex: %s",strerror(nRet));
|
Error("Failed locking shared data mutex: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
return nRet;
|
return nRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::unlock() {
|
int cURLCamera::unlock() {
|
||||||
int nRet;
|
int nRet;
|
||||||
|
|
||||||
/* Unlock shared data */
|
/* Unlock shared data */
|
||||||
nRet = pthread_mutex_unlock(&shareddata_mutex);
|
nRet = pthread_mutex_unlock(&shareddata_mutex);
|
||||||
if(nRet != 0) {
|
if(nRet != 0) {
|
||||||
Error("Failed unlocking shared data mutex: %s",strerror(nRet));
|
Error("Failed unlocking shared data mutex: %s",strerror(nRet));
|
||||||
}
|
}
|
||||||
return nRet;
|
return nRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||||
{
|
{
|
||||||
/* Signal the curl thread to terminate */
|
/* Signal the curl thread to terminate */
|
||||||
if(bTerminate)
|
if(bTerminate)
|
||||||
return -10;
|
return -10;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* These functions call the functions in the class for the correct object */
|
/* These functions call the functions in the class for the correct object */
|
||||||
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||||
{
|
{
|
||||||
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
|
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
|
||||||
{
|
{
|
||||||
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
|
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||||
{
|
{
|
||||||
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
|
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* thread_func_dispatcher(void* object) {
|
void* thread_func_dispatcher(void* object) {
|
||||||
return ((cURLCamera*)object)->thread_func();
|
return ((cURLCamera*)object)->thread_func();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,55 +42,55 @@
|
||||||
class cURLCamera : public Camera
|
class cURLCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
|
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
|
||||||
|
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
std::string mUser;
|
std::string mUser;
|
||||||
std::string mPass;
|
std::string mPass;
|
||||||
|
|
||||||
/* cURL object(s) */
|
/* cURL object(s) */
|
||||||
CURL* c;
|
CURL* c;
|
||||||
|
|
||||||
/* Shared data */
|
/* Shared data */
|
||||||
volatile bool bTerminate;
|
volatile bool bTerminate;
|
||||||
volatile bool bReset;
|
volatile bool bReset;
|
||||||
volatile mode_t mode;
|
volatile mode_t mode;
|
||||||
Buffer databuffer;
|
Buffer databuffer;
|
||||||
std::deque<size_t> single_offsets;
|
std::deque<size_t> single_offsets;
|
||||||
|
|
||||||
/* pthread objects */
|
/* pthread objects */
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_mutex_t shareddata_mutex;
|
pthread_mutex_t shareddata_mutex;
|
||||||
pthread_cond_t data_available_cond;
|
pthread_cond_t data_available_cond;
|
||||||
pthread_cond_t request_complete_cond;
|
pthread_cond_t request_complete_cond;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~cURLCamera();
|
~cURLCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
const std::string &Username() const { return( mUser ); }
|
const std::string &Username() const { return( mUser ); }
|
||||||
const std::string &Password() const { return( mPass ); }
|
const std::string &Password() const { return( mPass ); }
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
|
|
||||||
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||||
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
|
||||||
int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
|
int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||||
int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data);
|
int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data);
|
||||||
void* thread_func();
|
void* thread_func();
|
||||||
int lock();
|
int lock();
|
||||||
int unlock();
|
int unlock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int nRet;
|
int nRet;
|
||||||
CURLcode cRet;
|
CURLcode cRet;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
130
src/zm_db.cpp
130
src/zm_db.cpp
|
@ -29,85 +29,85 @@ int zmDbConnected = false;
|
||||||
|
|
||||||
void zmDbConnect()
|
void zmDbConnect()
|
||||||
{
|
{
|
||||||
if ( !mysql_init( &dbconn ) )
|
if ( !mysql_init( &dbconn ) )
|
||||||
{
|
{
|
||||||
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
my_bool reconnect = 1;
|
my_bool reconnect = 1;
|
||||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" );
|
||||||
if ( colonIndex != std::string::npos )
|
if ( colonIndex != std::string::npos )
|
||||||
|
{
|
||||||
|
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
||||||
|
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
||||||
|
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
||||||
{
|
{
|
||||||
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
|
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||||
std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 );
|
exit( mysql_errno( &dbconn ) );
|
||||||
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) )
|
|
||||||
{
|
|
||||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
||||||
{
|
{
|
||||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) )
|
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
||||||
{
|
exit( mysql_errno( &dbconn ) );
|
||||||
Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
|
|
||||||
exit( mysql_errno( &dbconn ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
}
|
||||||
{
|
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) )
|
||||||
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
{
|
||||||
exit( mysql_errno( &dbconn ) );
|
Error( "Can't select database: %s", mysql_error( &dbconn ) );
|
||||||
}
|
exit( mysql_errno( &dbconn ) );
|
||||||
zmDbConnected = true;
|
}
|
||||||
|
zmDbConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmDbClose()
|
void zmDbClose()
|
||||||
{
|
{
|
||||||
if ( zmDbConnected )
|
if ( zmDbConnected )
|
||||||
{
|
{
|
||||||
mysql_close( &dbconn );
|
mysql_close( &dbconn );
|
||||||
// mysql_init() call implicitly mysql_library_init() but
|
// mysql_init() call implicitly mysql_library_init() but
|
||||||
// mysql_close() does not call mysql_library_end()
|
// mysql_close() does not call mysql_library_end()
|
||||||
mysql_library_end();
|
mysql_library_end();
|
||||||
zmDbConnected = false;
|
zmDbConnected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES * zmDbFetch( const char * query ) {
|
MYSQL_RES * zmDbFetch( const char * query ) {
|
||||||
if ( ! zmDbConnected ) {
|
if ( ! zmDbConnected ) {
|
||||||
Error( "Not connected." );
|
Error( "Not connected." );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, query ) ) {
|
if ( mysql_query( &dbconn, query ) ) {
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
Debug( 4, "Success running query: %s", query );
|
Debug( 4, "Success running query: %s", query );
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result ) {
|
if ( !result ) {
|
||||||
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} // end MYSQL_RES * zmDbFetch( const char * query );
|
} // end MYSQL_RES * zmDbFetch( const char * query );
|
||||||
|
|
||||||
MYSQL_ROW zmDbFetchOne( const char *query ) {
|
MYSQL_ROW zmDbFetchOne( const char *query ) {
|
||||||
MYSQL_RES *result = zmDbFetch( query );
|
MYSQL_RES *result = zmDbFetch( query );
|
||||||
int n_rows = mysql_num_rows( result );
|
int n_rows = mysql_num_rows( result );
|
||||||
if ( n_rows != 1 ) {
|
if ( n_rows != 1 ) {
|
||||||
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
mysql_free_result( result );
|
mysql_free_result( result );
|
||||||
if ( ! dbrow ) {
|
if ( ! dbrow ) {
|
||||||
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return dbrow;
|
return dbrow;
|
||||||
}
|
}
|
||||||
|
|
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 Zone;
|
||||||
class Monitor;
|
class Monitor;
|
||||||
|
|
||||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||||
|
|
||||||
//
|
//
|
||||||
// Class describing events, i.e. captured periods of activity.
|
// Class describing events, i.e. captured periods of activity.
|
||||||
|
@ -51,219 +51,219 @@ class Event
|
||||||
friend class EventStream;
|
friend class EventStream;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool initialised;
|
static bool initialised;
|
||||||
static char capture_file_format[PATH_MAX];
|
static char capture_file_format[PATH_MAX];
|
||||||
static char analyse_file_format[PATH_MAX];
|
static char analyse_file_format[PATH_MAX];
|
||||||
static char general_file_format[PATH_MAX];
|
static char general_file_format[PATH_MAX];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static int sd;
|
static int sd;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::set<std::string> StringSet;
|
typedef std::set<std::string> StringSet;
|
||||||
typedef std::map<std::string,StringSet> StringSetMap;
|
typedef std::map<std::string,StringSet> StringSetMap;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
typedef enum { NORMAL, BULK, ALARM } FrameType;
|
||||||
|
|
||||||
struct PreAlarmData
|
struct PreAlarmData
|
||||||
{
|
{
|
||||||
Image *image;
|
Image *image;
|
||||||
struct timeval timestamp;
|
struct timeval timestamp;
|
||||||
unsigned int score;
|
unsigned int score;
|
||||||
Image *alarm_frame;
|
Image *alarm_frame;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int pre_alarm_count;
|
static int pre_alarm_count;
|
||||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
Monitor *monitor;
|
Monitor *monitor;
|
||||||
struct timeval start_time;
|
struct timeval start_time;
|
||||||
struct timeval end_time;
|
struct timeval end_time;
|
||||||
std::string cause;
|
std::string cause;
|
||||||
StringSetMap noteSetMap;
|
StringSetMap noteSetMap;
|
||||||
int frames;
|
int frames;
|
||||||
int alarm_frames;
|
int alarm_frames;
|
||||||
unsigned int tot_score;
|
unsigned int tot_score;
|
||||||
unsigned int max_score;
|
unsigned int max_score;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int last_db_frame;
|
int last_db_frame;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void Initialise()
|
static void Initialise()
|
||||||
{
|
{
|
||||||
if ( initialised )
|
if ( initialised )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
|
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
|
||||||
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
|
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
|
||||||
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
|
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
|
||||||
|
|
||||||
initialised = true;
|
initialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createNotes( std::string ¬es );
|
void createNotes( std::string ¬es );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool OpenFrameSocket( int );
|
static bool OpenFrameSocket( int );
|
||||||
static bool ValidateFrameSocket( int );
|
static bool ValidateFrameSocket( int );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
|
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
|
||||||
~Event();
|
~Event();
|
||||||
|
|
||||||
int Id() const { return( id ); }
|
int Id() const { return( id ); }
|
||||||
const std::string &Cause() { return( cause ); }
|
const std::string &Cause() { return( cause ); }
|
||||||
int Frames() const { return( frames ); }
|
int Frames() const { return( frames ); }
|
||||||
int AlarmFrames() const { return( alarm_frames ); }
|
int AlarmFrames() const { return( alarm_frames ); }
|
||||||
|
|
||||||
const struct timeval &StartTime() const { return( start_time ); }
|
const struct timeval &StartTime() const { return( start_time ); }
|
||||||
const struct timeval &EndTime() const { return( end_time ); }
|
const struct timeval &EndTime() const { return( end_time ); }
|
||||||
struct timeval &EndTime() { return( end_time ); }
|
struct timeval &EndTime() { return( end_time ); }
|
||||||
|
|
||||||
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
bool SendFrameImage( const Image *image, bool alarm_frame=false );
|
||||||
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
|
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
|
||||||
|
|
||||||
void updateNotes( const StringSetMap &stringSetMap );
|
void updateNotes( const StringSetMap &stringSetMap );
|
||||||
|
|
||||||
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
void AddFrames( int n_frames, Image **images, struct timeval **timestamps );
|
||||||
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const char *getSubPath( struct tm *time )
|
static const char *getSubPath( struct tm *time )
|
||||||
{
|
{
|
||||||
static char subpath[PATH_MAX] = "";
|
static char subpath[PATH_MAX] = "";
|
||||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||||
return( subpath );
|
return( subpath );
|
||||||
}
|
}
|
||||||
static const char *getSubPath( time_t *time )
|
static const char *getSubPath( time_t *time )
|
||||||
{
|
{
|
||||||
return( Event::getSubPath( localtime( time ) ) );
|
return( Event::getSubPath( localtime( time ) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static int PreAlarmCount()
|
static int PreAlarmCount()
|
||||||
{
|
{
|
||||||
return( pre_alarm_count );
|
return( pre_alarm_count );
|
||||||
}
|
}
|
||||||
static void EmptyPreAlarmFrames()
|
static void EmptyPreAlarmFrames()
|
||||||
{
|
{
|
||||||
if ( pre_alarm_count > 0 )
|
if ( pre_alarm_count > 0 )
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
|
||||||
{
|
{
|
||||||
delete pre_alarm_data[i].image;
|
delete pre_alarm_data[i].image;
|
||||||
delete pre_alarm_data[i].alarm_frame;
|
delete pre_alarm_data[i].alarm_frame;
|
||||||
}
|
}
|
||||||
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
|
||||||
}
|
}
|
||||||
pre_alarm_count = 0;
|
pre_alarm_count = 0;
|
||||||
}
|
}
|
||||||
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
|
||||||
{
|
{
|
||||||
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
pre_alarm_data[pre_alarm_count].image = new Image( *image );
|
||||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
||||||
pre_alarm_data[pre_alarm_count].score = score;
|
pre_alarm_data[pre_alarm_count].score = score;
|
||||||
if ( alarm_frame )
|
if ( alarm_frame )
|
||||||
{
|
{
|
||||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
|
||||||
}
|
}
|
||||||
pre_alarm_count++;
|
pre_alarm_count++;
|
||||||
}
|
}
|
||||||
void SavePreAlarmFrames()
|
void SavePreAlarmFrames()
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < pre_alarm_count; i++ )
|
for ( int i = 0; i < pre_alarm_count; i++ )
|
||||||
{
|
{
|
||||||
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
|
||||||
}
|
}
|
||||||
EmptyPreAlarmFrames();
|
EmptyPreAlarmFrames();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class EventStream : public StreamBase
|
class EventStream : public StreamBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct FrameData {
|
struct FrameData {
|
||||||
//unsigned long id;
|
//unsigned long id;
|
||||||
time_t timestamp;
|
time_t timestamp;
|
||||||
time_t offset;
|
time_t offset;
|
||||||
double delta;
|
double delta;
|
||||||
bool in_db;
|
bool in_db;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EventData
|
struct EventData
|
||||||
{
|
{
|
||||||
unsigned long event_id;
|
unsigned long event_id;
|
||||||
unsigned long monitor_id;
|
unsigned long monitor_id;
|
||||||
unsigned long frame_count;
|
unsigned long frame_count;
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
double duration;
|
double duration;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
int n_frames;
|
int n_frames;
|
||||||
FrameData *frames;
|
FrameData *frames;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||||
|
|
||||||
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
StreamMode mode;
|
StreamMode mode;
|
||||||
bool forceEventChange;
|
bool forceEventChange;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int curr_frame_id;
|
int curr_frame_id;
|
||||||
double curr_stream_time;
|
double curr_stream_time;
|
||||||
|
|
||||||
EventData *event_data;
|
EventData *event_data;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loadEventData( int event_id );
|
bool loadEventData( int event_id );
|
||||||
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id );
|
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id );
|
||||||
bool loadInitialEventData( int monitor_id, time_t event_time );
|
bool loadInitialEventData( int monitor_id, time_t event_time );
|
||||||
|
|
||||||
void checkEventLoaded();
|
void checkEventLoaded();
|
||||||
void processCommand( const CmdMsg *msg );
|
void processCommand( const CmdMsg *msg );
|
||||||
bool sendFrame( int delta_us );
|
bool sendFrame( int delta_us );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
EventStream()
|
EventStream()
|
||||||
{
|
{
|
||||||
mode = DEFAULT_MODE;
|
mode = DEFAULT_MODE;
|
||||||
|
|
||||||
forceEventChange = false;
|
forceEventChange = false;
|
||||||
|
|
||||||
curr_frame_id = 0;
|
curr_frame_id = 0;
|
||||||
curr_stream_time = 0.0;
|
curr_stream_time = 0.0;
|
||||||
|
|
||||||
event_data = 0;
|
event_data = 0;
|
||||||
}
|
}
|
||||||
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 )
|
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 )
|
||||||
{
|
{
|
||||||
loadInitialEventData( init_event_id, init_frame_id );
|
loadInitialEventData( init_event_id, init_frame_id );
|
||||||
loadMonitor( event_data->monitor_id );
|
loadMonitor( event_data->monitor_id );
|
||||||
}
|
}
|
||||||
void setStreamStart( int monitor_id, time_t event_time )
|
void setStreamStart( int monitor_id, time_t event_time )
|
||||||
{
|
{
|
||||||
loadInitialEventData( monitor_id, event_time );
|
loadInitialEventData( monitor_id, event_time );
|
||||||
loadMonitor( monitor_id );
|
loadMonitor( monitor_id );
|
||||||
}
|
}
|
||||||
void setStreamMode( StreamMode p_mode )
|
void setStreamMode( StreamMode p_mode )
|
||||||
{
|
{
|
||||||
mode = p_mode;
|
mode = p_mode;
|
||||||
}
|
}
|
||||||
void runStream();
|
void runStream();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EVENT_H
|
#endif // ZM_EVENT_H
|
||||||
|
|
|
@ -27,42 +27,42 @@
|
||||||
class Exception
|
class Exception
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
typedef enum { INFO, WARNING, ERROR, FATAL } Severity;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string mMessage;
|
std::string mMessage;
|
||||||
Severity mSeverity;
|
Severity mSeverity;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string &getMessage() const
|
const std::string &getMessage() const
|
||||||
{
|
{
|
||||||
return( mMessage );
|
return( mMessage );
|
||||||
}
|
}
|
||||||
Severity getSeverity() const
|
Severity getSeverity() const
|
||||||
{
|
{
|
||||||
return( mSeverity );
|
return( mSeverity );
|
||||||
}
|
}
|
||||||
bool isInfo() const
|
bool isInfo() const
|
||||||
{
|
{
|
||||||
return( mSeverity == INFO );
|
return( mSeverity == INFO );
|
||||||
}
|
}
|
||||||
bool isWarning() const
|
bool isWarning() const
|
||||||
{
|
{
|
||||||
return( mSeverity == WARNING );
|
return( mSeverity == WARNING );
|
||||||
}
|
}
|
||||||
bool isError() const
|
bool isError() const
|
||||||
{
|
{
|
||||||
return( mSeverity == ERROR );
|
return( mSeverity == ERROR );
|
||||||
}
|
}
|
||||||
bool isFatal() const
|
bool isFatal() const
|
||||||
{
|
{
|
||||||
return( mSeverity == FATAL );
|
return( mSeverity == FATAL );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EXCEPTION_H
|
#endif // ZM_EXCEPTION_H
|
||||||
|
|
|
@ -25,210 +25,210 @@
|
||||||
|
|
||||||
#if HAVE_LIBAVUTIL
|
#if HAVE_LIBAVUTIL
|
||||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
||||||
enum _AVPIXELFORMAT pf;
|
enum _AVPIXELFORMAT pf;
|
||||||
|
|
||||||
Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder);
|
Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder);
|
||||||
|
|
||||||
switch(p_colours) {
|
switch(p_colours) {
|
||||||
case ZM_COLOUR_RGB24:
|
case ZM_COLOUR_RGB24:
|
||||||
{
|
{
|
||||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||||
/* BGR subpixel order */
|
/* BGR subpixel order */
|
||||||
pf = AV_PIX_FMT_BGR24;
|
pf = AV_PIX_FMT_BGR24;
|
||||||
} else {
|
} else {
|
||||||
/* Assume RGB subpixel order */
|
/* Assume RGB subpixel order */
|
||||||
pf = AV_PIX_FMT_RGB24;
|
pf = AV_PIX_FMT_RGB24;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZM_COLOUR_RGB32:
|
case ZM_COLOUR_RGB32:
|
||||||
{
|
{
|
||||||
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||||
/* ARGB subpixel order */
|
/* ARGB subpixel order */
|
||||||
pf = AV_PIX_FMT_ARGB;
|
pf = AV_PIX_FMT_ARGB;
|
||||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||||
/* ABGR subpixel order */
|
/* ABGR subpixel order */
|
||||||
pf = AV_PIX_FMT_ABGR;
|
pf = AV_PIX_FMT_ABGR;
|
||||||
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||||
/* BGRA subpixel order */
|
/* BGRA subpixel order */
|
||||||
pf = AV_PIX_FMT_BGRA;
|
pf = AV_PIX_FMT_BGRA;
|
||||||
} else {
|
} else {
|
||||||
/* Assume RGBA subpixel order */
|
/* Assume RGBA subpixel order */
|
||||||
pf = AV_PIX_FMT_RGBA;
|
pf = AV_PIX_FMT_RGBA;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ZM_COLOUR_GRAY8:
|
case ZM_COLOUR_GRAY8:
|
||||||
pf = AV_PIX_FMT_GRAY8;
|
pf = AV_PIX_FMT_GRAY8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Panic("Unexpected colours: %d",p_colours);
|
Panic("Unexpected colours: %d",p_colours);
|
||||||
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
|
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pf;
|
return pf;
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBAVUTIL
|
#endif // HAVE_LIBAVUTIL
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||||
SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) {
|
SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) {
|
||||||
Debug(4,"SWScale object created");
|
Debug(4,"SWScale object created");
|
||||||
|
|
||||||
/* Allocate AVFrame for the input */
|
/* Allocate AVFrame for the input */
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
input_avframe = av_frame_alloc();
|
input_avframe = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
input_avframe = avcodec_alloc_frame();
|
input_avframe = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
if(input_avframe == NULL) {
|
if(input_avframe == NULL) {
|
||||||
Fatal("Failed allocating AVFrame for the input");
|
Fatal("Failed allocating AVFrame for the input");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate AVFrame for the output */
|
/* Allocate AVFrame for the output */
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
output_avframe = av_frame_alloc();
|
output_avframe = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
output_avframe = avcodec_alloc_frame();
|
output_avframe = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
if(output_avframe == NULL) {
|
if(output_avframe == NULL) {
|
||||||
Fatal("Failed allocating AVFrame for the output");
|
Fatal("Failed allocating AVFrame for the output");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SWScale::~SWScale() {
|
SWScale::~SWScale() {
|
||||||
|
|
||||||
/* Free up everything */
|
/* Free up everything */
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
av_frame_free( &input_avframe );
|
av_frame_free( &input_avframe );
|
||||||
#else
|
#else
|
||||||
av_freep( &input_avframe );
|
av_freep( &input_avframe );
|
||||||
#endif
|
#endif
|
||||||
//input_avframe = NULL;
|
//input_avframe = NULL;
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
av_frame_free( &output_avframe );
|
av_frame_free( &output_avframe );
|
||||||
#else
|
#else
|
||||||
av_freep( &output_avframe );
|
av_freep( &output_avframe );
|
||||||
#endif
|
#endif
|
||||||
//output_avframe = NULL;
|
//output_avframe = NULL;
|
||||||
|
|
||||||
if(swscale_ctx) {
|
if(swscale_ctx) {
|
||||||
sws_freeContext(swscale_ctx);
|
sws_freeContext(swscale_ctx);
|
||||||
swscale_ctx = NULL;
|
swscale_ctx = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(4,"SWScale object destroyed");
|
Debug(4,"SWScale object destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||||
|
|
||||||
/* Assign the defaults */
|
/* Assign the defaults */
|
||||||
default_input_pf = in_pf;
|
default_input_pf = in_pf;
|
||||||
default_output_pf = out_pf;
|
default_output_pf = out_pf;
|
||||||
default_width = width;
|
default_width = width;
|
||||||
default_height = height;
|
default_height = height;
|
||||||
|
|
||||||
gotdefaults = true;
|
gotdefaults = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||||
/* Parameter checking */
|
/* Parameter checking */
|
||||||
if(in_buffer == NULL || out_buffer == NULL) {
|
if(in_buffer == NULL || out_buffer == NULL) {
|
||||||
Error("NULL Input or output buffer");
|
Error("NULL Input or output buffer");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(in_pf == 0 || out_pf == 0) {
|
if(in_pf == 0 || out_pf == 0) {
|
||||||
Error("Invalid input or output pixel formats");
|
Error("Invalid input or output pixel formats");
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
if(!width || !height) {
|
if(!width || !height) {
|
||||||
Error("Invalid width or height");
|
Error("Invalid width or height");
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
||||||
/* Warn if the input or output pixelformat is not supported */
|
/* Warn if the input or output pixelformat is not supported */
|
||||||
if(!sws_isSupportedInput(in_pf)) {
|
if(!sws_isSupportedInput(in_pf)) {
|
||||||
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff));
|
||||||
}
|
}
|
||||||
if(!sws_isSupportedOutput(out_pf)) {
|
if(!sws_isSupportedOutput(out_pf)) {
|
||||||
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check the buffer sizes */
|
/* Check the buffer sizes */
|
||||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||||
if(insize != in_buffer_size) {
|
if(insize != in_buffer_size) {
|
||||||
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size);
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||||
if(outsize < out_buffer_size) {
|
if(outsize < out_buffer_size) {
|
||||||
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
|
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the context */
|
/* Get the context */
|
||||||
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
|
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
|
||||||
if(swscale_ctx == NULL) {
|
if(swscale_ctx == NULL) {
|
||||||
Error("Failed getting swscale context");
|
Error("Failed getting swscale context");
|
||||||
return -6;
|
return -6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill in the buffers */
|
/* Fill in the buffers */
|
||||||
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
|
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
|
||||||
Error("Failed filling input frame with input buffer");
|
Error("Failed filling input frame with input buffer");
|
||||||
return -7;
|
return -7;
|
||||||
}
|
}
|
||||||
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
|
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
|
||||||
Error("Failed filling output frame with output buffer");
|
Error("Failed filling output frame with output buffer");
|
||||||
return -8;
|
return -8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do the conversion */
|
/* Do the conversion */
|
||||||
if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) {
|
if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) {
|
||||||
Error("swscale conversion failed");
|
Error("swscale conversion failed");
|
||||||
return -10;
|
return -10;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) {
|
||||||
if(img->Width() != width) {
|
if(img->Width() != width) {
|
||||||
Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
|
Error("Source image width differs. Source: %d Output: %d",img->Width(), width);
|
||||||
return -12;
|
return -12;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(img->Height() != height) {
|
if(img->Height() != height) {
|
||||||
Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
|
Error("Source image height differs. Source: %d Output: %d",img->Height(), height);
|
||||||
return -13;
|
return -13;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height);
|
return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) {
|
int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) {
|
||||||
|
|
||||||
if(!gotdefaults) {
|
if(!gotdefaults) {
|
||||||
Error("Defaults are not set");
|
Error("Defaults are not set");
|
||||||
return -24;
|
return -24;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) {
|
int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) {
|
||||||
|
|
||||||
if(!gotdefaults) {
|
if(!gotdefaults) {
|
||||||
Error("Defaults are not set");
|
Error("Defaults are not set");
|
||||||
return -24;
|
return -24;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height);
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||||
|
|
||||||
|
|
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
|
* b and c the minor and micro versions of libav
|
||||||
* d and e the minor and micro versions of FFmpeg */
|
* d and e the minor and micro versions of FFmpeg */
|
||||||
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
|
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
|
||||||
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||||
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
|
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
|
||||||
#include <libavutil/opt.h>
|
#include <libavutil/opt.h>
|
||||||
|
@ -56,52 +56,52 @@ extern "C" {
|
||||||
|
|
||||||
#if defined(HAVE_LIBAVUTIL_AVUTIL_H)
|
#if defined(HAVE_LIBAVUTIL_AVUTIL_H)
|
||||||
#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100)
|
#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100)
|
||||||
#define _AVPIXELFORMAT AVPixelFormat
|
#define _AVPIXELFORMAT AVPixelFormat
|
||||||
#else
|
#else
|
||||||
#define _AVPIXELFORMAT PixelFormat
|
#define _AVPIXELFORMAT PixelFormat
|
||||||
#define AV_PIX_FMT_NONE PIX_FMT_NONE
|
#define AV_PIX_FMT_NONE PIX_FMT_NONE
|
||||||
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
|
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
|
||||||
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
|
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
|
||||||
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
|
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
|
||||||
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
|
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
|
||||||
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
|
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
|
||||||
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
|
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
|
||||||
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
|
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
|
||||||
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
|
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
|
||||||
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
|
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
|
||||||
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
|
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
|
||||||
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
|
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
|
||||||
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
|
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
|
||||||
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
|
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
|
||||||
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
|
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
|
||||||
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
|
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
|
||||||
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
|
||||||
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
|
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
|
||||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||||
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
|
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
|
||||||
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
|
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
|
||||||
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
|
||||||
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
|
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
|
||||||
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
|
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
|
||||||
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
|
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
|
||||||
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
|
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
|
||||||
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
|
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
|
||||||
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
|
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
|
||||||
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
|
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
|
||||||
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
|
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
|
||||||
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
|
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
|
||||||
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
|
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
|
||||||
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
|
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
|
||||||
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
|
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
|
||||||
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
|
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
|
||||||
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
|
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
|
||||||
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
|
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
|
||||||
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
|
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
|
||||||
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
|
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
|
||||||
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
|
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
|
||||||
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
|
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
|
||||||
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
|
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
|
||||||
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
|
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
|
||||||
#endif
|
#endif
|
||||||
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
|
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
|
||||||
|
|
||||||
|
@ -116,8 +116,8 @@ extern "C" {
|
||||||
* b and c the minor and micro versions of libav
|
* b and c the minor and micro versions of libav
|
||||||
* d and e the minor and micro versions of FFmpeg */
|
* d and e the minor and micro versions of FFmpeg */
|
||||||
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
|
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
|
||||||
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||||
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||||
|
|
||||||
#elif HAVE_FFMPEG_AVCODEC_H
|
#elif HAVE_FFMPEG_AVCODEC_H
|
||||||
#include <ffmpeg/avcodec.h>
|
#include <ffmpeg/avcodec.h>
|
||||||
|
@ -125,9 +125,9 @@ extern "C" {
|
||||||
|
|
||||||
#if defined(HAVE_LIBAVCODEC_AVCODEC_H)
|
#if defined(HAVE_LIBAVCODEC_AVCODEC_H)
|
||||||
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
|
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
|
||||||
#define _AVCODECID AVCodecID
|
#define _AVCODECID AVCodecID
|
||||||
#else
|
#else
|
||||||
#define _AVCODECID CodecID
|
#define _AVCODECID CodecID
|
||||||
#endif
|
#endif
|
||||||
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
|
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
|
||||||
|
|
||||||
|
@ -141,8 +141,8 @@ extern "C" {
|
||||||
* b and c the minor and micro versions of libav
|
* b and c the minor and micro versions of libav
|
||||||
* d and e the minor and micro versions of FFmpeg */
|
* d and e the minor and micro versions of FFmpeg */
|
||||||
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
|
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
|
||||||
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||||
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||||
|
|
||||||
#elif HAVE_FFMPEG_AVFORMAT_H
|
#elif HAVE_FFMPEG_AVFORMAT_H
|
||||||
#include <ffmpeg/avformat.h>
|
#include <ffmpeg/avformat.h>
|
||||||
|
@ -157,8 +157,8 @@ extern "C" {
|
||||||
* b and c the minor and micro versions of libav
|
* b and c the minor and micro versions of libav
|
||||||
* d and e the minor and micro versions of FFmpeg */
|
* d and e the minor and micro versions of FFmpeg */
|
||||||
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
|
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
|
||||||
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||||
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||||
|
|
||||||
#elif HAVE_FFMPEG_AVDEVICE_H
|
#elif HAVE_FFMPEG_AVDEVICE_H
|
||||||
#include <ffmpeg/avdevice.h>
|
#include <ffmpeg/avdevice.h>
|
||||||
|
@ -173,8 +173,8 @@ extern "C" {
|
||||||
* b and c the minor and micro versions of libav
|
* b and c the minor and micro versions of libav
|
||||||
* d and e the minor and micro versions of FFmpeg */
|
* d and e the minor and micro versions of FFmpeg */
|
||||||
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
|
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
|
||||||
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
|
||||||
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
|
||||||
|
|
||||||
#elif HAVE_FFMPEG_SWSCALE_H
|
#elif HAVE_FFMPEG_SWSCALE_H
|
||||||
#include <ffmpeg/swscale.h>
|
#include <ffmpeg/swscale.h>
|
||||||
|
@ -203,23 +203,23 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp
|
||||||
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||||
class SWScale {
|
class SWScale {
|
||||||
public:
|
public:
|
||||||
SWScale();
|
SWScale();
|
||||||
~SWScale();
|
~SWScale();
|
||||||
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||||
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
|
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||||
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
|
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
|
||||||
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||||
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool gotdefaults;
|
bool gotdefaults;
|
||||||
struct SwsContext* swscale_ctx;
|
struct SwsContext* swscale_ctx;
|
||||||
AVFrame* input_avframe;
|
AVFrame* input_avframe;
|
||||||
AVFrame* output_avframe;
|
AVFrame* output_avframe;
|
||||||
enum _AVPIXELFORMAT default_input_pf;
|
enum _AVPIXELFORMAT default_input_pf;
|
||||||
enum _AVPIXELFORMAT default_output_pf;
|
enum _AVPIXELFORMAT default_output_pf;
|
||||||
unsigned int default_width;
|
unsigned int default_width;
|
||||||
unsigned int default_height;
|
unsigned int default_height;
|
||||||
};
|
};
|
||||||
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||||
|
|
||||||
|
@ -256,21 +256,21 @@ protected:
|
||||||
*/
|
*/
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
inline static const std::string av_make_error_string(int errnum)
|
inline static const std::string av_make_error_string(int errnum)
|
||||||
{
|
{
|
||||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||||
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
|
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
|
||||||
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
#else
|
#else
|
||||||
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
|
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
|
||||||
#endif
|
#endif
|
||||||
return (std::string)errbuf;
|
return (std::string)errbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef av_err2str
|
#undef av_err2str
|
||||||
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
|
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )
|
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )
|
||||||
|
|
|
@ -28,71 +28,71 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SOLARIS
|
#ifdef SOLARIS
|
||||||
#include <sys/errno.h> // for ESRCH
|
#include <sys/errno.h> // for ESRCH
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
mMethod( p_method ),
|
mMethod( p_method ),
|
||||||
mOptions( p_options )
|
mOptions( p_options )
|
||||||
{
|
{
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mCodecContext = NULL;
|
mCodecContext = NULL;
|
||||||
mCodec = NULL;
|
mCodec = NULL;
|
||||||
mRawFrame = NULL;
|
mRawFrame = NULL;
|
||||||
mFrame = NULL;
|
mFrame = NULL;
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
mIsOpening = false;
|
mIsOpening = false;
|
||||||
mCanCapture = false;
|
mCanCapture = false;
|
||||||
mOpenStart = 0;
|
mOpenStart = 0;
|
||||||
mReopenThread = 0;
|
mReopenThread = 0;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||||
if(colours == ZM_COLOUR_RGB32) {
|
if(colours == ZM_COLOUR_RGB32) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||||
} else if(colours == ZM_COLOUR_RGB24) {
|
} else if(colours == ZM_COLOUR_RGB24) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||||
} else {
|
} else {
|
||||||
Panic("Unexpected colours: %d",colours);
|
Panic("Unexpected colours: %d",colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FfmpegCamera::~FfmpegCamera()
|
FfmpegCamera::~FfmpegCamera()
|
||||||
{
|
{
|
||||||
CloseFfmpeg();
|
CloseFfmpeg();
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FfmpegCamera::Initialise()
|
void FfmpegCamera::Initialise()
|
||||||
{
|
{
|
||||||
if ( logDebugging() )
|
if ( logDebugging() )
|
||||||
av_log_set_level( AV_LOG_DEBUG );
|
av_log_set_level( AV_LOG_DEBUG );
|
||||||
else
|
else
|
||||||
av_log_set_level( AV_LOG_QUIET );
|
av_log_set_level( AV_LOG_QUIET );
|
||||||
|
|
||||||
av_register_all();
|
av_register_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FfmpegCamera::Terminate()
|
void FfmpegCamera::Terminate()
|
||||||
|
@ -101,373 +101,373 @@ void FfmpegCamera::Terminate()
|
||||||
|
|
||||||
int FfmpegCamera::PrimeCapture()
|
int FfmpegCamera::PrimeCapture()
|
||||||
{
|
{
|
||||||
Info( "Priming capture from %s", mPath.c_str() );
|
Info( "Priming capture from %s", mPath.c_str() );
|
||||||
|
|
||||||
if (OpenFfmpeg() != 0){
|
if (OpenFfmpeg() != 0){
|
||||||
ReopenFfmpeg();
|
ReopenFfmpeg();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::PreCapture()
|
int FfmpegCamera::PreCapture()
|
||||||
{
|
{
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::Capture( Image &image )
|
int FfmpegCamera::Capture( Image &image )
|
||||||
{
|
{
|
||||||
if (!mCanCapture){
|
if (!mCanCapture){
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
||||||
|
if (mReopenThread != 0) {
|
||||||
|
void *retval = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pthread_join(mReopenThread, &retval);
|
||||||
|
if (ret != 0){
|
||||||
|
Error("Could not join reopen thread.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
Info( "Successfully reopened stream." );
|
||||||
if (mReopenThread != 0) {
|
mReopenThread = 0;
|
||||||
void *retval = 0;
|
}
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = pthread_join(mReopenThread, &retval);
|
AVPacket packet;
|
||||||
if (ret != 0){
|
uint8_t* directbuffer;
|
||||||
Error("Could not join reopen thread.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Info( "Successfully reopened stream." );
|
/* Request a writeable buffer of the target image */
|
||||||
mReopenThread = 0;
|
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
}
|
if(directbuffer == NULL) {
|
||||||
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
AVPacket packet;
|
int frameComplete = false;
|
||||||
uint8_t* directbuffer;
|
while ( !frameComplete )
|
||||||
|
{
|
||||||
/* Request a writeable buffer of the target image */
|
int avResult = av_read_frame( mFormatContext, &packet );
|
||||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
if ( avResult < 0 )
|
||||||
if(directbuffer == NULL) {
|
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int frameComplete = false;
|
|
||||||
while ( !frameComplete )
|
|
||||||
{
|
{
|
||||||
int avResult = av_read_frame( mFormatContext, &packet );
|
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||||
if ( avResult < 0 )
|
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
{
|
if (
|
||||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
// Check if EOF.
|
||||||
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
||||||
if (
|
// Check for Connection failure.
|
||||||
// Check if EOF.
|
(avResult == -110)
|
||||||
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
|
)
|
||||||
// Check for Connection failure.
|
{
|
||||||
(avResult == -110)
|
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
||||||
)
|
ReopenFfmpeg();
|
||||||
{
|
}
|
||||||
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
|
|
||||||
ReopenFfmpeg();
|
|
||||||
}
|
|
||||||
|
|
||||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
Debug( 5, "Got packet from stream %d", packet.stream_index );
|
||||||
if ( packet.stream_index == mVideoStreamId )
|
if ( packet.stream_index == mVideoStreamId )
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
|
||||||
#else
|
#else
|
||||||
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
|
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
|
||||||
#endif
|
#endif
|
||||||
Fatal( "Unable to decode frame at frame %d", frameCount );
|
Fatal( "Unable to decode frame at frame %d", frameCount );
|
||||||
|
|
||||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
||||||
|
|
||||||
if ( frameComplete )
|
if ( frameComplete )
|
||||||
{
|
{
|
||||||
Debug( 3, "Got frame %d", frameCount );
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if(mConvertContext == NULL) {
|
if(mConvertContext == NULL) {
|
||||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
|
|
||||||
if(mConvertContext == NULL)
|
if(mConvertContext == NULL)
|
||||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||||
}
|
|
||||||
|
|
||||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
|
||||||
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 ( 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
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
mCanCapture = true;
|
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() {
|
int FfmpegCamera::ReopenFfmpeg() {
|
||||||
|
|
||||||
Debug(2, "ReopenFfmpeg called.");
|
Debug(2, "ReopenFfmpeg called.");
|
||||||
|
|
||||||
mCanCapture = false;
|
mCanCapture = false;
|
||||||
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
||||||
// Log a fatal error and exit the process.
|
// Log a fatal error and exit the process.
|
||||||
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::CloseFfmpeg(){
|
int FfmpegCamera::CloseFfmpeg(){
|
||||||
|
|
||||||
Debug(2, "CloseFfmpeg called.");
|
Debug(2, "CloseFfmpeg called.");
|
||||||
|
|
||||||
mCanCapture = false;
|
mCanCapture = false;
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
av_frame_free( &mFrame );
|
av_frame_free( &mFrame );
|
||||||
av_frame_free( &mRawFrame );
|
av_frame_free( &mRawFrame );
|
||||||
#else
|
#else
|
||||||
av_freep( &mFrame );
|
av_freep( &mFrame );
|
||||||
av_freep( &mRawFrame );
|
av_freep( &mRawFrame );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if ( mConvertContext )
|
if ( mConvertContext )
|
||||||
{
|
{
|
||||||
sws_freeContext( mConvertContext );
|
sws_freeContext( mConvertContext );
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( mCodecContext )
|
if ( mCodecContext )
|
||||||
{
|
{
|
||||||
avcodec_close( mCodecContext );
|
avcodec_close( mCodecContext );
|
||||||
mCodecContext = NULL; // Freed by av_close_input_file
|
mCodecContext = NULL; // Freed by av_close_input_file
|
||||||
}
|
}
|
||||||
if ( mFormatContext )
|
if ( mFormatContext )
|
||||||
{
|
{
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
|
||||||
av_close_input_file( mFormatContext );
|
av_close_input_file( mFormatContext );
|
||||||
#else
|
#else
|
||||||
avformat_close_input( &mFormatContext );
|
avformat_close_input( &mFormatContext );
|
||||||
#endif
|
#endif
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
|
int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
|
||||||
{
|
{
|
||||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||||
if (camera->mIsOpening){
|
if (camera->mIsOpening){
|
||||||
int now = time(NULL);
|
int now = time(NULL);
|
||||||
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
|
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
|
||||||
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||||
if (ctx == NULL) return NULL;
|
if (ctx == NULL) return NULL;
|
||||||
|
|
||||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||||
|
|
||||||
while (1){
|
while (1){
|
||||||
// Close current stream.
|
// Close current stream.
|
||||||
camera->CloseFfmpeg();
|
camera->CloseFfmpeg();
|
||||||
|
|
||||||
// Sleep if necessary to not reconnect too fast.
|
// Sleep if necessary to not reconnect too fast.
|
||||||
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
||||||
wait = wait < 0 ? 0 : wait;
|
wait = wait < 0 ? 0 : wait;
|
||||||
if (wait > 0){
|
if (wait > 0){
|
||||||
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
||||||
sleep(wait);
|
sleep(wait);
|
||||||
}
|
|
||||||
|
|
||||||
if (camera->OpenFfmpeg() == 0){
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (camera->OpenFfmpeg() == 0){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -33,51 +33,51 @@
|
||||||
class FfmpegCamera : public Camera
|
class FfmpegCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
std::string mMethod;
|
std::string mMethod;
|
||||||
std::string mOptions;
|
std::string mOptions;
|
||||||
|
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
#if HAVE_LIBAVFORMAT
|
#if HAVE_LIBAVFORMAT
|
||||||
AVFormatContext *mFormatContext;
|
AVFormatContext *mFormatContext;
|
||||||
int mVideoStreamId;
|
int mVideoStreamId;
|
||||||
AVCodecContext *mCodecContext;
|
AVCodecContext *mCodecContext;
|
||||||
AVCodec *mCodec;
|
AVCodec *mCodec;
|
||||||
AVFrame *mRawFrame;
|
AVFrame *mRawFrame;
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
|
|
||||||
int OpenFfmpeg();
|
int OpenFfmpeg();
|
||||||
int ReopenFfmpeg();
|
int ReopenFfmpeg();
|
||||||
int CloseFfmpeg();
|
int CloseFfmpeg();
|
||||||
static int FfmpegInterruptCallback(void *ctx);
|
static int FfmpegInterruptCallback(void *ctx);
|
||||||
static void* ReopenFfmpegThreadCallback(void *ctx);
|
static void* ReopenFfmpegThreadCallback(void *ctx);
|
||||||
bool mIsOpening;
|
bool mIsOpening;
|
||||||
bool mCanCapture;
|
bool mCanCapture;
|
||||||
int mOpenStart;
|
int mOpenStart;
|
||||||
pthread_t mReopenThread;
|
pthread_t mReopenThread;
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext;
|
struct SwsContext *mConvertContext;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~FfmpegCamera();
|
~FfmpegCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
const std::string &Options() const { return( mOptions ); }
|
const std::string &Options() const { return( mOptions ); }
|
||||||
const std::string &Method() const { return( mMethod ); }
|
const std::string &Method() const { return( mMethod ); }
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_FFMPEG_CAMERA_H
|
#endif // ZM_FFMPEG_CAMERA_H
|
||||||
|
|
|
@ -36,28 +36,28 @@
|
||||||
|
|
||||||
FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture )
|
FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture )
|
||||||
{
|
{
|
||||||
strncpy( path, p_path, sizeof(path) );
|
strncpy( path, p_path, sizeof(path) );
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FileCamera::~FileCamera()
|
FileCamera::~FileCamera()
|
||||||
{
|
{
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCamera::Initialise()
|
void FileCamera::Initialise()
|
||||||
{
|
{
|
||||||
if ( !path[0] )
|
if ( !path[0] )
|
||||||
{
|
{
|
||||||
Error( "No path specified for file image" );
|
Error( "No path specified for file image" );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileCamera::Terminate()
|
void FileCamera::Terminate()
|
||||||
|
@ -66,26 +66,26 @@ void FileCamera::Terminate()
|
||||||
|
|
||||||
int FileCamera::PreCapture()
|
int FileCamera::PreCapture()
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if ( stat( path, &statbuf ) < 0 )
|
if ( stat( path, &statbuf ) < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't stat %s: %s", path, strerror(errno) );
|
Error( "Can't stat %s: %s", path, strerror(errno) );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( (time( 0 ) - statbuf.st_mtime) < 1 )
|
while ( (time( 0 ) - statbuf.st_mtime) < 1 )
|
||||||
{
|
{
|
||||||
usleep( 100000 );
|
usleep( 100000 );
|
||||||
}
|
}
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileCamera::Capture( Image &image )
|
int FileCamera::Capture( Image &image )
|
||||||
{
|
{
|
||||||
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
|
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileCamera::PostCapture()
|
int FileCamera::PostCapture()
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,19 +33,19 @@
|
||||||
class FileCamera : public Camera
|
class FileCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~FileCamera();
|
~FileCamera();
|
||||||
|
|
||||||
const char *Path() const { return( path ); }
|
const char *Path() const { return( path ); }
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_FILE_CAMERA_H
|
#endif // ZM_FILE_CAMERA_H
|
||||||
|
|
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)
|
ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||||
{
|
{
|
||||||
m_Detectors = source.m_Detectors;
|
m_Detectors = source.m_Detectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source)
|
||||||
*/
|
*/
|
||||||
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source)
|
||||||
{
|
{
|
||||||
m_Detectors = source.m_Detectors;
|
m_Detectors = source.m_Detectors;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ImageAnalyser::~ImageAnalyser()
|
ImageAnalyser::~ImageAnalyser()
|
||||||
{
|
{
|
||||||
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
for(DetectorsList::reverse_iterator It = m_Detectors.rbegin();
|
||||||
It != m_Detectors.rend();
|
It != m_Detectors.rend();
|
||||||
++It)
|
++It)
|
||||||
delete *It;
|
delete *It;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser()
|
||||||
*/
|
*/
|
||||||
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause)
|
||||||
{
|
{
|
||||||
Event::StringSet zoneSet;
|
Event::StringSet zoneSet;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
|
||||||
for(DetectorsList::iterator It = m_Detectors.begin();
|
for(DetectorsList::iterator It = m_Detectors.begin();
|
||||||
It != m_Detectors.end();
|
It != m_Detectors.end();
|
||||||
++It)
|
++It)
|
||||||
|
{
|
||||||
|
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
||||||
|
if (detect_score)
|
||||||
{
|
{
|
||||||
int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet);
|
score += detect_score;
|
||||||
if (detect_score)
|
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
||||||
{
|
if (det_cause.length())
|
||||||
score += detect_score;
|
det_cause += ", ";
|
||||||
noteSetMap[(*It)->getDetectionCause()] = zoneSet;
|
det_cause += (*It)->getDetectionCause();
|
||||||
if (det_cause.length())
|
|
||||||
det_cause += ", ";
|
|
||||||
det_cause += (*It)->getDetectionCause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return score;
|
}
|
||||||
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,17 +23,17 @@ using namespace std;
|
||||||
class ImageAnalyser {
|
class ImageAnalyser {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//!Default constructor.
|
//!Default constructor.
|
||||||
ImageAnalyser() {};
|
ImageAnalyser() {};
|
||||||
|
|
||||||
//! Destructor.
|
//! Destructor.
|
||||||
~ImageAnalyser();
|
~ImageAnalyser();
|
||||||
|
|
||||||
//! Copy constructor.
|
//! Copy constructor.
|
||||||
ImageAnalyser(const ImageAnalyser& source);
|
ImageAnalyser(const ImageAnalyser& source);
|
||||||
|
|
||||||
//! Overloaded operator=.
|
//! Overloaded operator=.
|
||||||
ImageAnalyser& operator=(const ImageAnalyser& source);
|
ImageAnalyser& operator=(const ImageAnalyser& source);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
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 )
|
void zm_jpeg_error_exit( j_common_ptr cinfo )
|
||||||
{
|
{
|
||||||
static char buffer[JMSG_LENGTH_MAX];
|
static char buffer[JMSG_LENGTH_MAX];
|
||||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||||
|
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
|
|
||||||
Error( "%s", buffer );
|
Error( "%s", buffer );
|
||||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
if ( ++jpeg_err_count == MAX_JPEG_ERRS )
|
||||||
{
|
{
|
||||||
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count );
|
||||||
}
|
}
|
||||||
|
|
||||||
longjmp( zmerr->setjmp_buffer, 1 );
|
longjmp( zmerr->setjmp_buffer, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level )
|
||||||
{
|
{
|
||||||
static char buffer[JMSG_LENGTH_MAX];
|
static char buffer[JMSG_LENGTH_MAX];
|
||||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||||
|
|
||||||
if ( msg_level < 0 )
|
if ( msg_level < 0 )
|
||||||
{
|
{
|
||||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||||
* the policy implemented here is to show only the first warning,
|
* the policy implemented here is to show only the first warning,
|
||||||
* unless trace_level >= 3.
|
* unless trace_level >= 3.
|
||||||
*/
|
*/
|
||||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 )
|
||||||
{
|
{
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||||
Warning( "%s", buffer );
|
Warning( "%s", buffer );
|
||||||
}
|
}
|
||||||
/* Always count warnings in num_warnings. */
|
/* Always count warnings in num_warnings. */
|
||||||
zmerr->pub.num_warnings++;
|
zmerr->pub.num_warnings++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||||
if ( zmerr->pub.trace_level >= msg_level )
|
if ( zmerr->pub.trace_level >= msg_level )
|
||||||
{
|
{
|
||||||
(zmerr->pub.format_message)( cinfo, buffer );
|
(zmerr->pub.format_message)( cinfo, buffer );
|
||||||
Debug( msg_level, "%s", buffer );
|
Debug( msg_level, "%s", buffer );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expanded data destination object for memory */
|
/* Expanded data destination object for memory */
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
struct jpeg_destination_mgr pub; /* public fields */
|
struct jpeg_destination_mgr pub; /* public fields */
|
||||||
|
|
||||||
JOCTET *outbuffer; /* target buffer */
|
JOCTET *outbuffer; /* target buffer */
|
||||||
int *outbuffer_size;
|
int *outbuffer_size;
|
||||||
JOCTET *buffer; /* start of buffer */
|
JOCTET *buffer; /* start of buffer */
|
||||||
} mem_destination_mgr;
|
} mem_destination_mgr;
|
||||||
|
|
||||||
typedef mem_destination_mgr * mem_dest_ptr;
|
typedef mem_destination_mgr * mem_dest_ptr;
|
||||||
|
|
||||||
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize destination --- called by jpeg_start_compress
|
* Initialize destination --- called by jpeg_start_compress
|
||||||
|
@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr;
|
||||||
|
|
||||||
static void init_destination (j_compress_ptr cinfo)
|
static void init_destination (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
|
|
||||||
/* Allocate the output buffer --- it will be released when done with image */
|
/* Allocate the output buffer --- it will be released when done with image */
|
||||||
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
|
||||||
|
|
||||||
dest->pub.next_output_byte = dest->buffer;
|
dest->pub.next_output_byte = dest->buffer;
|
||||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
*(dest->outbuffer_size) = 0;
|
*(dest->outbuffer_size) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo)
|
||||||
|
|
||||||
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
|
|
||||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE );
|
||||||
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
*(dest->outbuffer_size) += OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
dest->pub.next_output_byte = dest->buffer;
|
dest->pub.next_output_byte = dest->buffer;
|
||||||
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
|
||||||
|
|
||||||
return( TRUE );
|
return( TRUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo)
|
||||||
|
|
||||||
static void term_destination (j_compress_ptr cinfo)
|
static void term_destination (j_compress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
|
||||||
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
|
||||||
|
|
||||||
if ( datacount > 0 )
|
if ( datacount > 0 )
|
||||||
{
|
{
|
||||||
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount );
|
||||||
*(dest->outbuffer_size) += datacount;
|
*(dest->outbuffer_size) += datacount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo)
|
||||||
|
|
||||||
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size )
|
||||||
{
|
{
|
||||||
mem_dest_ptr dest;
|
mem_dest_ptr dest;
|
||||||
|
|
||||||
/* The destination object is made permanent so that multiple JPEG images
|
/* The destination object is made permanent so that multiple JPEG images
|
||||||
* can be written to the same file without re-executing jpeg_stdio_dest.
|
* can be written to the same file without re-executing jpeg_stdio_dest.
|
||||||
* This makes it dangerous to use this manager and a different destination
|
* This makes it dangerous to use this manager and a different destination
|
||||||
* manager serially with the same JPEG object, because their private object
|
* manager serially with the same JPEG object, because their private object
|
||||||
* sizes may be different. Caveat programmer.
|
* sizes may be different. Caveat programmer.
|
||||||
*/
|
*/
|
||||||
if ( cinfo->dest == NULL )
|
if ( cinfo->dest == NULL )
|
||||||
{
|
{
|
||||||
/* first time for this JPEG object? */
|
/* first time for this JPEG object? */
|
||||||
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr));
|
||||||
}
|
}
|
||||||
|
|
||||||
dest = (mem_dest_ptr) cinfo->dest;
|
dest = (mem_dest_ptr) cinfo->dest;
|
||||||
dest->pub.init_destination = init_destination;
|
dest->pub.init_destination = init_destination;
|
||||||
dest->pub.empty_output_buffer = empty_output_buffer;
|
dest->pub.empty_output_buffer = empty_output_buffer;
|
||||||
dest->pub.term_destination = term_destination;
|
dest->pub.term_destination = term_destination;
|
||||||
dest->outbuffer = outbuffer;
|
dest->outbuffer = outbuffer;
|
||||||
dest->outbuffer_size = outbuffer_size;
|
dest->outbuffer_size = outbuffer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Expanded data source object for memory input */
|
/* Expanded data source object for memory input */
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
struct jpeg_source_mgr pub; /* public fields */
|
struct jpeg_source_mgr pub; /* public fields */
|
||||||
|
|
||||||
JOCTET * inbuffer; /* source stream */
|
JOCTET * inbuffer; /* source stream */
|
||||||
int inbuffer_size;
|
int inbuffer_size;
|
||||||
int inbuffer_size_hwm; /* High water mark */
|
int inbuffer_size_hwm; /* High water mark */
|
||||||
|
|
||||||
JOCTET * buffer; /* start of buffer */
|
JOCTET * buffer; /* start of buffer */
|
||||||
boolean start_of_data; /* have we gotten any data yet? */
|
boolean start_of_data; /* have we gotten any data yet? */
|
||||||
} mem_source_mgr;
|
} mem_source_mgr;
|
||||||
|
|
||||||
typedef mem_source_mgr * mem_src_ptr;
|
typedef mem_source_mgr * mem_src_ptr;
|
||||||
|
|
||||||
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize source --- called by jpeg_read_header
|
* Initialize source --- called by jpeg_read_header
|
||||||
|
@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr;
|
||||||
|
|
||||||
static void init_source (j_decompress_ptr cinfo)
|
static void init_source (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
|
|
||||||
/* We reset the empty-input-file flag for each image,
|
/* We reset the empty-input-file flag for each image,
|
||||||
* but we don't clear the input buffer.
|
* but we don't clear the input buffer.
|
||||||
* This is correct behavior for reading a series of images from one source.
|
* This is correct behavior for reading a series of images from one source.
|
||||||
*/
|
*/
|
||||||
src->start_of_data = TRUE;
|
src->start_of_data = TRUE;
|
||||||
src->pub.bytes_in_buffer = 0;
|
src->pub.bytes_in_buffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo)
|
||||||
|
|
||||||
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
size_t nbytes;
|
size_t nbytes;
|
||||||
|
|
||||||
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size );
|
||||||
nbytes = src->inbuffer_size;
|
nbytes = src->inbuffer_size;
|
||||||
|
|
||||||
if ( nbytes <= 0 )
|
if ( nbytes <= 0 )
|
||||||
{
|
{
|
||||||
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
if ( src->start_of_data ) /* Treat empty input file as fatal error */
|
||||||
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
||||||
WARNMS(cinfo, JWRN_JPEG_EOF);
|
WARNMS(cinfo, JWRN_JPEG_EOF);
|
||||||
/* Insert a fake EOI marker */
|
/* Insert a fake EOI marker */
|
||||||
src->buffer[0] = (JOCTET) 0xFF;
|
src->buffer[0] = (JOCTET) 0xFF;
|
||||||
src->buffer[1] = (JOCTET) JPEG_EOI;
|
src->buffer[1] = (JOCTET) JPEG_EOI;
|
||||||
nbytes = 2;
|
nbytes = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
src->pub.next_input_byte = src->buffer;
|
src->pub.next_input_byte = src->buffer;
|
||||||
src->pub.bytes_in_buffer = nbytes;
|
src->pub.bytes_in_buffer = nbytes;
|
||||||
src->start_of_data = FALSE;
|
src->start_of_data = FALSE;
|
||||||
|
|
||||||
return( TRUE );
|
return( TRUE );
|
||||||
}
|
}
|
||||||
|
@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo)
|
||||||
|
|
||||||
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||||
{
|
{
|
||||||
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
mem_src_ptr src = (mem_src_ptr) cinfo->src;
|
||||||
|
|
||||||
/* Just a dumb implementation for now. Could use fseek() except
|
/* Just a dumb implementation for now. Could use fseek() except
|
||||||
* it doesn't work on pipes. Not clear that being smart is worth
|
* it doesn't work on pipes. Not clear that being smart is worth
|
||||||
* any trouble anyway --- large skips are infrequent.
|
* any trouble anyway --- large skips are infrequent.
|
||||||
*/
|
*/
|
||||||
if ( num_bytes > 0 )
|
if ( num_bytes > 0 )
|
||||||
|
{
|
||||||
|
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
||||||
{
|
{
|
||||||
while ( num_bytes > (long) src->pub.bytes_in_buffer )
|
num_bytes -= (long) src->pub.bytes_in_buffer;
|
||||||
{
|
(void) fill_input_buffer(cinfo);
|
||||||
num_bytes -= (long) src->pub.bytes_in_buffer;
|
/* note we assume that fill_input_buffer will never return FALSE,
|
||||||
(void) fill_input_buffer(cinfo);
|
* so suspension need not be handled.
|
||||||
/* note we assume that fill_input_buffer will never return FALSE,
|
*/
|
||||||
* so suspension need not be handled.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
src->pub.next_input_byte += (size_t) num_bytes;
|
|
||||||
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
|
||||||
}
|
}
|
||||||
|
src->pub.next_input_byte += (size_t) num_bytes;
|
||||||
|
src->pub.bytes_in_buffer -= (size_t) num_bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
|
||||||
|
|
||||||
static void term_source (j_decompress_ptr cinfo)
|
static void term_source (j_decompress_ptr cinfo)
|
||||||
{
|
{
|
||||||
/* no work necessary here */
|
/* no work necessary here */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -354,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 )
|
void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||||
{
|
{
|
||||||
mem_src_ptr src;
|
mem_src_ptr src;
|
||||||
|
|
||||||
/* The source object and input buffer are made permanent so that a series
|
|
||||||
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
|
||||||
* only before the first one. (If we discarded the buffer at the end of
|
|
||||||
* one image, we'd likely lose the start of the next one.)
|
|
||||||
* This makes it unsafe to use this manager and a different source
|
|
||||||
* manager serially with the same JPEG object. Caveat programmer.
|
|
||||||
*/
|
|
||||||
if ( cinfo->src == NULL )
|
|
||||||
{
|
|
||||||
/* first time for this JPEG object? */
|
|
||||||
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
|
||||||
src = (mem_src_ptr) cinfo->src;
|
|
||||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
|
||||||
src->inbuffer_size_hwm = inbuffer_size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
src = (mem_src_ptr) cinfo->src;
|
|
||||||
if ( src->inbuffer_size_hwm < inbuffer_size )
|
|
||||||
{
|
|
||||||
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
|
||||||
src->inbuffer_size_hwm = inbuffer_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* The source object and input buffer are made permanent so that a series
|
||||||
|
* of JPEG images can be read from the same file by calling zm_jpeg_mem_src
|
||||||
|
* only before the first one. (If we discarded the buffer at the end of
|
||||||
|
* one image, we'd likely lose the start of the next one.)
|
||||||
|
* This makes it unsafe to use this manager and a different source
|
||||||
|
* manager serially with the same JPEG object. Caveat programmer.
|
||||||
|
*/
|
||||||
|
if ( cinfo->src == NULL )
|
||||||
|
{
|
||||||
|
/* first time for this JPEG object? */
|
||||||
|
cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr));
|
||||||
src = (mem_src_ptr) cinfo->src;
|
src = (mem_src_ptr) cinfo->src;
|
||||||
src->pub.init_source = init_source;
|
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||||
src->pub.fill_input_buffer = fill_input_buffer;
|
src->inbuffer_size_hwm = inbuffer_size;
|
||||||
src->pub.skip_input_data = skip_input_data;
|
}
|
||||||
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
else
|
||||||
src->pub.term_source = term_source;
|
{
|
||||||
src->inbuffer = (JOCTET *)inbuffer;
|
src = (mem_src_ptr) cinfo->src;
|
||||||
src->inbuffer_size = inbuffer_size;
|
if ( src->inbuffer_size_hwm < inbuffer_size )
|
||||||
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
{
|
||||||
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET));
|
||||||
|
src->inbuffer_size_hwm = inbuffer_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src = (mem_src_ptr) cinfo->src;
|
||||||
|
src->pub.init_source = init_source;
|
||||||
|
src->pub.fill_input_buffer = fill_input_buffer;
|
||||||
|
src->pub.skip_input_data = skip_input_data;
|
||||||
|
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
|
||||||
|
src->pub.term_source = term_source;
|
||||||
|
src->inbuffer = (JOCTET *)inbuffer;
|
||||||
|
src->inbuffer_size = inbuffer_size;
|
||||||
|
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
|
||||||
|
src->pub.next_input_byte = NULL; /* until buffer loaded */
|
||||||
}
|
}
|
||||||
|
|
||||||
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
void zm_use_std_huff_tables( j_decompress_ptr cinfo ) {
|
||||||
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
/* JPEG standard Huffman tables (cf. JPEG standard section K.3) */
|
||||||
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
/* IMPORTANT: these are only valid for 8-bit data precision! */
|
||||||
static const JHUFF_TBL dclumin = {
|
static const JHUFF_TBL dclumin = {
|
||||||
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL dcchrome = {
|
static const JHUFF_TBL dcchrome = {
|
||||||
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
|
||||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL aclumin = {
|
static const JHUFF_TBL aclumin = {
|
||||||
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d },
|
||||||
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
|
||||||
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
||||||
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
||||||
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
||||||
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
|
||||||
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
||||||
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||||
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||||
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||||
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||||
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||||
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||||
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
|
||||||
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
||||||
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
||||||
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
||||||
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
|
||||||
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
||||||
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
|
||||||
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||||
0xf9, 0xfa },
|
0xf9, 0xfa },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
static const JHUFF_TBL acchrome = {
|
static const JHUFF_TBL acchrome = {
|
||||||
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 },
|
||||||
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
|
||||||
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
||||||
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
||||||
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
||||||
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
|
||||||
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
||||||
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
|
||||||
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||||
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
||||||
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
||||||
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||||
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
|
||||||
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
||||||
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
||||||
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
||||||
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
|
||||||
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
||||||
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
|
||||||
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
||||||
0xf9, 0xfa },
|
0xf9, 0xfa },
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
|
|
||||||
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin;
|
||||||
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome;
|
||||||
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin;
|
||||||
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,8 @@ extern "C"
|
||||||
/* Stuff for overriden error handlers */
|
/* Stuff for overriden error handlers */
|
||||||
struct zm_error_mgr
|
struct zm_error_mgr
|
||||||
{
|
{
|
||||||
struct jpeg_error_mgr pub;
|
struct jpeg_error_mgr pub;
|
||||||
jmp_buf setjmp_buffer;
|
jmp_buf setjmp_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct zm_error_mgr *zm_error_ptr;
|
typedef struct zm_error_mgr *zm_error_ptr;
|
||||||
|
|
|
@ -25,102 +25,102 @@
|
||||||
// Do all the buffer checking work here to avoid unnecessary locking
|
// Do all the buffer checking work here to avoid unnecessary locking
|
||||||
void* LibvlcLockBuffer(void* opaque, void** planes)
|
void* LibvlcLockBuffer(void* opaque, void** planes)
|
||||||
{
|
{
|
||||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||||
data->mutex.lock();
|
data->mutex.lock();
|
||||||
|
|
||||||
uint8_t* buffer = data->buffer;
|
uint8_t* buffer = data->buffer;
|
||||||
data->buffer = data->prevBuffer;
|
data->buffer = data->prevBuffer;
|
||||||
data->prevBuffer = buffer;
|
data->prevBuffer = buffer;
|
||||||
|
|
||||||
*planes = data->buffer;
|
*planes = data->buffer;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes)
|
void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes)
|
||||||
{
|
{
|
||||||
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
LibvlcPrivateData* data = (LibvlcPrivateData*)opaque;
|
||||||
|
|
||||||
bool newFrame = false;
|
bool newFrame = false;
|
||||||
for(uint32_t i = 0; i < data->bufferSize; i++)
|
for(uint32_t i = 0; i < data->bufferSize; i++)
|
||||||
|
{
|
||||||
|
if(data->buffer[i] != data->prevBuffer[i])
|
||||||
{
|
{
|
||||||
if(data->buffer[i] != data->prevBuffer[i])
|
newFrame = true;
|
||||||
{
|
break;
|
||||||
newFrame = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
data->mutex.unlock();
|
}
|
||||||
|
data->mutex.unlock();
|
||||||
|
|
||||||
time_t now;
|
time_t now;
|
||||||
time(&now);
|
time(&now);
|
||||||
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
|
// Return frames slightly faster than 1fps (if time() supports greater than one second resolution)
|
||||||
if(newFrame || difftime(now, data->prevTime) >= 0.8)
|
if(newFrame || difftime(now, data->prevTime) >= 0.8)
|
||||||
{
|
{
|
||||||
data->prevTime = now;
|
data->prevTime = now;
|
||||||
data->newImage.updateValueSignal(true);
|
data->newImage.updateValueSignal(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
mMethod( p_method ),
|
mMethod( p_method ),
|
||||||
mOptions( p_options )
|
mOptions( p_options )
|
||||||
{
|
{
|
||||||
mLibvlcInstance = NULL;
|
mLibvlcInstance = NULL;
|
||||||
mLibvlcMedia = NULL;
|
mLibvlcMedia = NULL;
|
||||||
mLibvlcMediaPlayer = NULL;
|
mLibvlcMediaPlayer = NULL;
|
||||||
mLibvlcData.buffer = NULL;
|
mLibvlcData.buffer = NULL;
|
||||||
mLibvlcData.prevBuffer = NULL;
|
mLibvlcData.prevBuffer = NULL;
|
||||||
|
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||||
if(colours == ZM_COLOUR_RGB32) {
|
if(colours == ZM_COLOUR_RGB32) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||||
mTargetChroma = "RV32";
|
mTargetChroma = "RV32";
|
||||||
mBpp = 4;
|
mBpp = 4;
|
||||||
} else if(colours == ZM_COLOUR_RGB24) {
|
} else if(colours == ZM_COLOUR_RGB24) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||||
mTargetChroma = "RV24";
|
mTargetChroma = "RV24";
|
||||||
mBpp = 3;
|
mBpp = 3;
|
||||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
mTargetChroma = "GREY";
|
mTargetChroma = "GREY";
|
||||||
mBpp = 1;
|
mBpp = 1;
|
||||||
} else {
|
} else {
|
||||||
Panic("Unexpected colours: %d",colours);
|
Panic("Unexpected colours: %d",colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LibvlcCamera::~LibvlcCamera()
|
LibvlcCamera::~LibvlcCamera()
|
||||||
{
|
{
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
if(mLibvlcMediaPlayer != NULL)
|
if(mLibvlcMediaPlayer != NULL)
|
||||||
{
|
{
|
||||||
libvlc_media_player_release(mLibvlcMediaPlayer);
|
libvlc_media_player_release(mLibvlcMediaPlayer);
|
||||||
mLibvlcMediaPlayer = NULL;
|
mLibvlcMediaPlayer = NULL;
|
||||||
}
|
}
|
||||||
if(mLibvlcMedia != NULL)
|
if(mLibvlcMedia != NULL)
|
||||||
{
|
{
|
||||||
libvlc_media_release(mLibvlcMedia);
|
libvlc_media_release(mLibvlcMedia);
|
||||||
mLibvlcMedia = NULL;
|
mLibvlcMedia = NULL;
|
||||||
}
|
}
|
||||||
if(mLibvlcInstance != NULL)
|
if(mLibvlcInstance != NULL)
|
||||||
{
|
{
|
||||||
libvlc_release(mLibvlcInstance);
|
libvlc_release(mLibvlcInstance);
|
||||||
mLibvlcInstance = NULL;
|
mLibvlcInstance = NULL;
|
||||||
}
|
}
|
||||||
if (mOptArgV != NULL)
|
if (mOptArgV != NULL)
|
||||||
{
|
{
|
||||||
delete[] mOptArgV;
|
delete[] mOptArgV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibvlcCamera::Initialise()
|
void LibvlcCamera::Initialise()
|
||||||
|
@ -129,91 +129,91 @@ void LibvlcCamera::Initialise()
|
||||||
|
|
||||||
void LibvlcCamera::Terminate()
|
void LibvlcCamera::Terminate()
|
||||||
{
|
{
|
||||||
libvlc_media_player_stop(mLibvlcMediaPlayer);
|
libvlc_media_player_stop(mLibvlcMediaPlayer);
|
||||||
if(mLibvlcData.buffer != NULL)
|
if(mLibvlcData.buffer != NULL)
|
||||||
{
|
{
|
||||||
zm_freealigned(mLibvlcData.buffer);
|
zm_freealigned(mLibvlcData.buffer);
|
||||||
}
|
}
|
||||||
if(mLibvlcData.prevBuffer != NULL)
|
if(mLibvlcData.prevBuffer != NULL)
|
||||||
{
|
{
|
||||||
zm_freealigned(mLibvlcData.prevBuffer);
|
zm_freealigned(mLibvlcData.prevBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int LibvlcCamera::PrimeCapture()
|
int LibvlcCamera::PrimeCapture()
|
||||||
{
|
{
|
||||||
Info("Priming capture from %s", mPath.c_str());
|
Info("Priming capture from %s", mPath.c_str());
|
||||||
|
|
||||||
StringVector opVect = split(Options(), ",");
|
StringVector opVect = split(Options(), ",");
|
||||||
|
|
||||||
// Set transport method as specified by method field, rtpUni is default
|
// Set transport method as specified by method field, rtpUni is default
|
||||||
if ( Method() == "rtpMulti" )
|
if ( Method() == "rtpMulti" )
|
||||||
opVect.push_back("--rtsp-mcast");
|
opVect.push_back("--rtsp-mcast");
|
||||||
else if ( Method() == "rtpRtsp" )
|
else if ( Method() == "rtpRtsp" )
|
||||||
opVect.push_back("--rtsp-tcp");
|
opVect.push_back("--rtsp-tcp");
|
||||||
else if ( Method() == "rtpRtspHttp" )
|
else if ( Method() == "rtpRtspHttp" )
|
||||||
opVect.push_back("--rtsp-http");
|
opVect.push_back("--rtsp-http");
|
||||||
|
|
||||||
if (opVect.size() > 0)
|
if (opVect.size() > 0)
|
||||||
{
|
{
|
||||||
mOptArgV = new char*[opVect.size()];
|
mOptArgV = new char*[opVect.size()];
|
||||||
Debug(2, "Number of Options: %d",opVect.size());
|
Debug(2, "Number of Options: %d",opVect.size());
|
||||||
for (size_t i=0; i< opVect.size(); i++) {
|
for (size_t i=0; i< opVect.size(); i++) {
|
||||||
opVect[i] = trimSpaces(opVect[i]);
|
opVect[i] = trimSpaces(opVect[i]);
|
||||||
mOptArgV[i] = (char *)opVect[i].c_str();
|
mOptArgV[i] = (char *)opVect[i].c_str();
|
||||||
Debug(2, "set option %d to '%s'", i, opVect[i].c_str());
|
Debug(2, "set option %d to '%s'", i, opVect[i].c_str());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV);
|
mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV);
|
||||||
if(mLibvlcInstance == NULL)
|
if(mLibvlcInstance == NULL)
|
||||||
Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg());
|
Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg());
|
||||||
|
|
||||||
mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str());
|
mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str());
|
||||||
if(mLibvlcMedia == NULL)
|
if(mLibvlcMedia == NULL)
|
||||||
Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||||
|
|
||||||
mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia);
|
mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia);
|
||||||
if(mLibvlcMediaPlayer == NULL)
|
if(mLibvlcMediaPlayer == NULL)
|
||||||
Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg());
|
||||||
|
|
||||||
libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
|
libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp);
|
||||||
libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData);
|
libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData);
|
||||||
|
|
||||||
mLibvlcData.bufferSize = width * height * mBpp;
|
mLibvlcData.bufferSize = width * height * mBpp;
|
||||||
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
|
// Libvlc wants 32 byte alignment for images (should in theory do this for all image lines)
|
||||||
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||||
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize);
|
||||||
|
|
||||||
mLibvlcData.newImage.setValueImmediate(false);
|
mLibvlcData.newImage.setValueImmediate(false);
|
||||||
|
|
||||||
libvlc_media_player_play(mLibvlcMediaPlayer);
|
libvlc_media_player_play(mLibvlcMediaPlayer);
|
||||||
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LibvlcCamera::PreCapture()
|
int LibvlcCamera::PreCapture()
|
||||||
{
|
{
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not return -1 as cancels capture. Always wait for image if available.
|
// Should not return -1 as cancels capture. Always wait for image if available.
|
||||||
int LibvlcCamera::Capture( Image &image )
|
int LibvlcCamera::Capture( Image &image )
|
||||||
{
|
{
|
||||||
while(!mLibvlcData.newImage.getValueImmediate())
|
while(!mLibvlcData.newImage.getValueImmediate())
|
||||||
mLibvlcData.newImage.getUpdatedValue(1);
|
mLibvlcData.newImage.getUpdatedValue(1);
|
||||||
|
|
||||||
mLibvlcData.mutex.lock();
|
mLibvlcData.mutex.lock();
|
||||||
image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp);
|
image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp);
|
||||||
mLibvlcData.newImage.setValueImmediate(false);
|
mLibvlcData.newImage.setValueImmediate(false);
|
||||||
mLibvlcData.mutex.unlock();
|
mLibvlcData.mutex.unlock();
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LibvlcCamera::PostCapture()
|
int LibvlcCamera::PostCapture()
|
||||||
{
|
{
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBVLC
|
#endif // HAVE_LIBVLC
|
||||||
|
|
|
@ -33,44 +33,44 @@
|
||||||
// Used by libvlc callbacks
|
// Used by libvlc callbacks
|
||||||
struct LibvlcPrivateData
|
struct LibvlcPrivateData
|
||||||
{
|
{
|
||||||
uint8_t* buffer;
|
uint8_t* buffer;
|
||||||
uint8_t* prevBuffer;
|
uint8_t* prevBuffer;
|
||||||
time_t prevTime;
|
time_t prevTime;
|
||||||
uint32_t bufferSize;
|
uint32_t bufferSize;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
ThreadData<bool> newImage;
|
ThreadData<bool> newImage;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LibvlcCamera : public Camera
|
class LibvlcCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
std::string mMethod;
|
std::string mMethod;
|
||||||
std::string mOptions;
|
std::string mOptions;
|
||||||
char **mOptArgV;
|
char **mOptArgV;
|
||||||
LibvlcPrivateData mLibvlcData;
|
LibvlcPrivateData mLibvlcData;
|
||||||
std::string mTargetChroma;
|
std::string mTargetChroma;
|
||||||
uint8_t mBpp;
|
uint8_t mBpp;
|
||||||
|
|
||||||
libvlc_instance_t *mLibvlcInstance;
|
libvlc_instance_t *mLibvlcInstance;
|
||||||
libvlc_media_t *mLibvlcMedia;
|
libvlc_media_t *mLibvlcMedia;
|
||||||
libvlc_media_player_t *mLibvlcMediaPlayer;
|
libvlc_media_player_t *mLibvlcMediaPlayer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~LibvlcCamera();
|
~LibvlcCamera();
|
||||||
|
|
||||||
const std::string &Path() const { return( mPath ); }
|
const std::string &Path() const { return( mPath ); }
|
||||||
const std::string &Options() const { return( mOptions ); }
|
const std::string &Options() const { return( mOptions ); }
|
||||||
const std::string &Method() const { return( mMethod ); }
|
const std::string &Method() const { return( mMethod ); }
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBVLC
|
#endif // HAVE_LIBVLC
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -47,99 +47,99 @@ class LocalCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
#if ZM_HAS_V4L2
|
#if ZM_HAS_V4L2
|
||||||
struct V4L2MappedBuffer
|
struct V4L2MappedBuffer
|
||||||
{
|
{
|
||||||
void *start;
|
void *start;
|
||||||
size_t length;
|
size_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V4L2Data
|
struct V4L2Data
|
||||||
{
|
{
|
||||||
v4l2_cropcap cropcap;
|
v4l2_cropcap cropcap;
|
||||||
v4l2_crop crop;
|
v4l2_crop crop;
|
||||||
v4l2_format fmt;
|
v4l2_format fmt;
|
||||||
v4l2_requestbuffers reqbufs;
|
v4l2_requestbuffers reqbufs;
|
||||||
V4L2MappedBuffer *buffers;
|
V4L2MappedBuffer *buffers;
|
||||||
v4l2_buffer *bufptr;
|
v4l2_buffer *bufptr;
|
||||||
};
|
};
|
||||||
#endif // ZM_HAS_V4L2
|
#endif // ZM_HAS_V4L2
|
||||||
|
|
||||||
#if ZM_HAS_V4L1
|
#if ZM_HAS_V4L1
|
||||||
struct V4L1Data
|
struct V4L1Data
|
||||||
{
|
{
|
||||||
int active_frame;
|
int active_frame;
|
||||||
video_mbuf frames;
|
video_mbuf frames;
|
||||||
video_mmap *buffers;
|
video_mmap *buffers;
|
||||||
unsigned char *bufptr;
|
unsigned char *bufptr;
|
||||||
};
|
};
|
||||||
#endif // ZM_HAS_V4L1
|
#endif // ZM_HAS_V4L1
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string device;
|
std::string device;
|
||||||
int channel;
|
int channel;
|
||||||
int standard;
|
int standard;
|
||||||
int palette;
|
int palette;
|
||||||
bool device_prime;
|
bool device_prime;
|
||||||
bool channel_prime;
|
bool channel_prime;
|
||||||
int channel_index;
|
int channel_index;
|
||||||
unsigned int extras;
|
unsigned int extras;
|
||||||
|
|
||||||
unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */
|
unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */
|
||||||
convert_fptr_t conversion_fptr; /* Pointer to conversion function used */
|
convert_fptr_t conversion_fptr; /* Pointer to conversion function used */
|
||||||
|
|
||||||
uint32_t AutoSelectFormat(int p_colours);
|
uint32_t AutoSelectFormat(int p_colours);
|
||||||
|
|
||||||
static int camera_count;
|
static int camera_count;
|
||||||
static int channel_count;
|
static int channel_count;
|
||||||
static int channels[VIDEO_MAX_FRAME];
|
static int channels[VIDEO_MAX_FRAME];
|
||||||
static int standards[VIDEO_MAX_FRAME];
|
static int standards[VIDEO_MAX_FRAME];
|
||||||
static int vid_fd;
|
static int vid_fd;
|
||||||
static int v4l_version;
|
static int v4l_version;
|
||||||
bool v4l_multi_buffer;
|
bool v4l_multi_buffer;
|
||||||
unsigned int v4l_captures_per_frame;
|
unsigned int v4l_captures_per_frame;
|
||||||
|
|
||||||
#if ZM_HAS_V4L2
|
#if ZM_HAS_V4L2
|
||||||
static V4L2Data v4l2_data;
|
static V4L2Data v4l2_data;
|
||||||
#endif // ZM_HAS_V4L2
|
#endif // ZM_HAS_V4L2
|
||||||
#if ZM_HAS_V4L1
|
#if ZM_HAS_V4L1
|
||||||
static V4L1Data v4l1_data;
|
static V4L1Data v4l1_data;
|
||||||
#endif // ZM_HAS_V4L1
|
#endif // ZM_HAS_V4L1
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
static AVFrame **capturePictures;
|
static AVFrame **capturePictures;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
_AVPIXELFORMAT capturePixFormat;
|
_AVPIXELFORMAT capturePixFormat;
|
||||||
struct SwsContext *imgConversionContext;
|
struct SwsContext *imgConversionContext;
|
||||||
AVFrame *tmpPicture;
|
AVFrame *tmpPicture;
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
static LocalCamera *last_camera;
|
static LocalCamera *last_camera;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0);
|
LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0);
|
||||||
~LocalCamera();
|
~LocalCamera();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
|
|
||||||
const std::string &Device() const { return( device ); }
|
const std::string &Device() const { return( device ); }
|
||||||
|
|
||||||
int Channel() const { return( channel ); }
|
int Channel() const { return( channel ); }
|
||||||
int Standard() const { return( standard ); }
|
int Standard() const { return( standard ); }
|
||||||
int Palette() const { return( palette ); }
|
int Palette() const { return( palette ); }
|
||||||
int Extras() const { return( extras ); }
|
int Extras() const { return( extras ); }
|
||||||
|
|
||||||
int Brightness( int p_brightness=-1 );
|
int Brightness( int p_brightness=-1 );
|
||||||
int Hue( int p_hue=-1 );
|
int Hue( int p_hue=-1 );
|
||||||
int Colour( int p_colour=-1 );
|
int Colour( int p_colour=-1 );
|
||||||
int Contrast( int p_contrast=-1 );
|
int Contrast( int p_contrast=-1 );
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
|
|
||||||
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
298
src/zm_logger.h
298
src/zm_logger.h
|
@ -33,190 +33,190 @@
|
||||||
class Logger
|
class Logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
NOOPT=-6,
|
NOOPT=-6,
|
||||||
NOLOG,
|
NOLOG,
|
||||||
PANIC,
|
PANIC,
|
||||||
FATAL,
|
FATAL,
|
||||||
ERROR,
|
ERROR,
|
||||||
WARNING,
|
WARNING,
|
||||||
INFO,
|
INFO,
|
||||||
DEBUG1,
|
DEBUG1,
|
||||||
DEBUG2,
|
DEBUG2,
|
||||||
DEBUG3,
|
DEBUG3,
|
||||||
DEBUG4,
|
DEBUG4,
|
||||||
DEBUG5,
|
DEBUG5,
|
||||||
DEBUG6,
|
DEBUG6,
|
||||||
DEBUG7,
|
DEBUG7,
|
||||||
DEBUG8,
|
DEBUG8,
|
||||||
DEBUG9
|
DEBUG9
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int Level;
|
typedef int Level;
|
||||||
|
|
||||||
typedef std::map<Level,std::string> StringMap;
|
typedef std::map<Level,std::string> StringMap;
|
||||||
typedef std::map<Level,int> IntMap;
|
typedef std::map<Level,int> IntMap;
|
||||||
|
|
||||||
class Options
|
class Options
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int mTermLevel;
|
int mTermLevel;
|
||||||
int mDatabaseLevel;
|
int mDatabaseLevel;
|
||||||
int mFileLevel;
|
int mFileLevel;
|
||||||
int mSyslogLevel;
|
int mSyslogLevel;
|
||||||
|
|
||||||
std::string mLogPath;
|
|
||||||
std::string mLogFile;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
|
||||||
mTermLevel( termLevel ),
|
|
||||||
mDatabaseLevel( databaseLevel ),
|
|
||||||
mFileLevel( fileLevel ),
|
|
||||||
mSyslogLevel( syslogLevel ),
|
|
||||||
mLogPath( logPath ),
|
|
||||||
mLogFile( logFile )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool smInitialised;
|
|
||||||
static Logger *smInstance;
|
|
||||||
|
|
||||||
static StringMap smCodes;
|
|
||||||
static IntMap smSyslogPriorities;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool mInitialised;
|
|
||||||
|
|
||||||
std::string mId;
|
|
||||||
std::string mIdRoot;
|
|
||||||
std::string mIdArgs;
|
|
||||||
|
|
||||||
Level mLevel; // Level that is currently in operation
|
|
||||||
Level mTermLevel; // Maximum level output via terminal
|
|
||||||
Level mDatabaseLevel; // Maximum level output via database
|
|
||||||
Level mFileLevel; // Maximum level output via file
|
|
||||||
Level mSyslogLevel; // Maximum level output via syslog
|
|
||||||
Level mEffectiveLevel; // Level optimised to take account of maxima
|
|
||||||
|
|
||||||
bool mDbConnected;
|
|
||||||
MYSQL mDbConnection;
|
|
||||||
std::string mLogPath;
|
std::string mLogPath;
|
||||||
std::string mLogFile;
|
std::string mLogFile;
|
||||||
FILE *mLogFileFP;
|
|
||||||
|
|
||||||
bool mHasTerm;
|
public:
|
||||||
bool mFlush;
|
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
|
||||||
|
mTermLevel( termLevel ),
|
||||||
|
mDatabaseLevel( databaseLevel ),
|
||||||
|
mFileLevel( fileLevel ),
|
||||||
|
mSyslogLevel( syslogLevel ),
|
||||||
|
mLogPath( logPath ),
|
||||||
|
mLogFile( logFile )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void usrHandler( int sig );
|
static bool smInitialised;
|
||||||
|
static Logger *smInstance;
|
||||||
|
|
||||||
public:
|
static StringMap smCodes;
|
||||||
friend void logInit( const char *name, const Options &options );
|
static IntMap smSyslogPriorities;
|
||||||
friend void logTerm();
|
|
||||||
|
|
||||||
static Logger *fetch()
|
|
||||||
{
|
|
||||||
if ( !smInstance )
|
|
||||||
{
|
|
||||||
smInstance = new Logger();
|
|
||||||
Options options;
|
|
||||||
smInstance->initialise( "undef", options );
|
|
||||||
}
|
|
||||||
return( smInstance );
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Logger();
|
bool mInitialised;
|
||||||
~Logger();
|
|
||||||
|
|
||||||
public:
|
std::string mId;
|
||||||
void initialise( const std::string &id, const Options &options );
|
std::string mIdRoot;
|
||||||
void terminate();
|
std::string mIdArgs;
|
||||||
|
|
||||||
|
Level mLevel; // Level that is currently in operation
|
||||||
|
Level mTermLevel; // Maximum level output via terminal
|
||||||
|
Level mDatabaseLevel; // Maximum level output via database
|
||||||
|
Level mFileLevel; // Maximum level output via file
|
||||||
|
Level mSyslogLevel; // Maximum level output via syslog
|
||||||
|
Level mEffectiveLevel; // Level optimised to take account of maxima
|
||||||
|
|
||||||
|
bool mDbConnected;
|
||||||
|
MYSQL mDbConnection;
|
||||||
|
std::string mLogPath;
|
||||||
|
std::string mLogFile;
|
||||||
|
FILE *mLogFileFP;
|
||||||
|
|
||||||
|
bool mHasTerm;
|
||||||
|
bool mFlush;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int limit( int level )
|
static void usrHandler( int sig );
|
||||||
{
|
|
||||||
if ( level > DEBUG9 )
|
|
||||||
return( DEBUG9 );
|
|
||||||
if ( level < NOLOG )
|
|
||||||
return( NOLOG );
|
|
||||||
return( level );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolEnv( const std::string &name, bool defaultValue=false );
|
|
||||||
int intEnv( const std::string &name, bool defaultValue=0 );
|
|
||||||
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
|
||||||
char *getTargettedEnv( const std::string &name );
|
|
||||||
|
|
||||||
void loadEnv();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string &id() const
|
friend void logInit( const char *name, const Options &options );
|
||||||
|
friend void logTerm();
|
||||||
|
|
||||||
|
static Logger *fetch()
|
||||||
|
{
|
||||||
|
if ( !smInstance )
|
||||||
{
|
{
|
||||||
return( mId );
|
smInstance = new Logger();
|
||||||
|
Options options;
|
||||||
|
smInstance->initialise( "undef", options );
|
||||||
}
|
}
|
||||||
|
return( smInstance );
|
||||||
const std::string &id( const std::string &id );
|
}
|
||||||
|
|
||||||
Level level() const
|
|
||||||
{
|
|
||||||
return( mLevel );
|
|
||||||
}
|
|
||||||
Level level( Level=NOOPT );
|
|
||||||
|
|
||||||
bool debugOn()
|
|
||||||
{
|
|
||||||
return( mEffectiveLevel >= DEBUG1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
Level termLevel( Level=NOOPT );
|
|
||||||
Level databaseLevel( Level=NOOPT );
|
|
||||||
Level fileLevel( Level=NOOPT );
|
|
||||||
Level syslogLevel( Level=NOOPT );
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void logFile( const std::string &logFile );
|
Logger();
|
||||||
void openFile();
|
~Logger();
|
||||||
void closeFile();
|
|
||||||
void openSyslog();
|
|
||||||
void closeSyslog();
|
|
||||||
void closeDatabase();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
void initialise( const std::string &id, const Options &options );
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int limit( int level )
|
||||||
|
{
|
||||||
|
if ( level > DEBUG9 )
|
||||||
|
return( DEBUG9 );
|
||||||
|
if ( level < NOLOG )
|
||||||
|
return( NOLOG );
|
||||||
|
return( level );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool boolEnv( const std::string &name, bool defaultValue=false );
|
||||||
|
int intEnv( const std::string &name, bool defaultValue=0 );
|
||||||
|
std::string strEnv( const std::string &name, const std::string defaultValue="" );
|
||||||
|
char *getTargettedEnv( const std::string &name );
|
||||||
|
|
||||||
|
void loadEnv();
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string &id() const
|
||||||
|
{
|
||||||
|
return( mId );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &id( const std::string &id );
|
||||||
|
|
||||||
|
Level level() const
|
||||||
|
{
|
||||||
|
return( mLevel );
|
||||||
|
}
|
||||||
|
Level level( Level=NOOPT );
|
||||||
|
|
||||||
|
bool debugOn()
|
||||||
|
{
|
||||||
|
return( mEffectiveLevel >= DEBUG1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Level termLevel( Level=NOOPT );
|
||||||
|
Level databaseLevel( Level=NOOPT );
|
||||||
|
Level fileLevel( Level=NOOPT );
|
||||||
|
Level syslogLevel( Level=NOOPT );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void logFile( const std::string &logFile );
|
||||||
|
void openFile();
|
||||||
|
void closeFile();
|
||||||
|
void openSyslog();
|
||||||
|
void closeSyslog();
|
||||||
|
void closeDatabase();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... );
|
||||||
};
|
};
|
||||||
|
|
||||||
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
|
||||||
void logTerm();
|
void logTerm();
|
||||||
inline const std::string &logId()
|
inline const std::string &logId()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->id() );
|
return( Logger::fetch()->id() );
|
||||||
}
|
}
|
||||||
inline Logger::Level logLevel()
|
inline Logger::Level logLevel()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->level() );
|
return( Logger::fetch()->level() );
|
||||||
}
|
}
|
||||||
inline void logCapLevel( Logger::Level level )
|
inline void logCapLevel( Logger::Level level )
|
||||||
{
|
{
|
||||||
Logger::fetch()->level( level );
|
Logger::fetch()->level( level );
|
||||||
}
|
}
|
||||||
inline Logger::Level logDebugging()
|
inline Logger::Level logDebugging()
|
||||||
{
|
{
|
||||||
return( Logger::fetch()->debugOn() );
|
return( Logger::fetch()->debugOn() );
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logPrintf(logLevel,params...) {\
|
#define logPrintf(logLevel,params...) {\
|
||||||
if ( logLevel <= Logger::fetch()->level() )\
|
if ( logLevel <= Logger::fetch()->level() )\
|
||||||
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define logHexdump(logLevel,data,len) {\
|
#define logHexdump(logLevel,data,len) {\
|
||||||
if ( logLevel <= Logger::fetch()->level() )\
|
if ( logLevel <= Logger::fetch()->level() )\
|
||||||
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Debug compiled out */
|
/* Debug compiled out */
|
||||||
#ifndef DBG_OFF
|
#ifndef DBG_OFF
|
||||||
|
@ -228,16 +228,16 @@ inline Logger::Level logDebugging()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Standard debug calls */
|
/* Standard debug calls */
|
||||||
#define Info(params...) logPrintf(Logger::INFO,##params)
|
#define Info(params...) logPrintf(Logger::INFO,##params)
|
||||||
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
#define Warning(params...) logPrintf(Logger::WARNING,##params)
|
||||||
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
#define Error(params...) logPrintf(Logger::ERROR,##params)
|
||||||
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
#define Fatal(params...) logPrintf(Logger::FATAL,##params)
|
||||||
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
#define Panic(params...) logPrintf(Logger::PANIC,##params)
|
||||||
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__)
|
||||||
#define Log() Info("Log")
|
#define Log() Info("Log")
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__))
|
||||||
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__))
|
||||||
#else
|
#else
|
||||||
#define Enter(level)
|
#define Enter(level)
|
||||||
#define Exit(level)
|
#define Exit(level)
|
||||||
|
|
|
@ -24,138 +24,138 @@
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
|
|
||||||
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) {
|
||||||
uint8_t* retptr;
|
uint8_t* retptr;
|
||||||
#if HAVE_POSIX_MEMALIGN
|
#if HAVE_POSIX_MEMALIGN
|
||||||
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return retptr;
|
return retptr;
|
||||||
#else
|
#else
|
||||||
uint8_t* alloc;
|
uint8_t* alloc;
|
||||||
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*));
|
||||||
|
|
||||||
if(retptr == NULL)
|
if(retptr == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
alloc = retptr + sizeof(void*);
|
alloc = retptr + sizeof(void*);
|
||||||
|
|
||||||
if(((long)alloc % reqalignment) != 0)
|
if(((long)alloc % reqalignment) != 0)
|
||||||
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
alloc = alloc + (reqalignment - ((long)alloc % reqalignment));
|
||||||
|
|
||||||
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
/* Store a pointer before to the start of the block, just before returned aligned memory */
|
||||||
*(void**)(alloc - sizeof(void*)) = retptr;
|
*(void**)(alloc - sizeof(void*)) = retptr;
|
||||||
|
|
||||||
return alloc;
|
return alloc;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void zm_freealigned(void* ptr) {
|
inline void zm_freealigned(void* ptr) {
|
||||||
#if HAVE_POSIX_MEMALIGN
|
#if HAVE_POSIX_MEMALIGN
|
||||||
free(ptr);
|
free(ptr);
|
||||||
#else
|
#else
|
||||||
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
/* Start of block is stored before the block if it was allocated by zm_mallocaligned */
|
||||||
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
free(*(void**)((uint8_t*)ptr - sizeof(void*)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
inline char *mempbrk( register const char *s, const char *accept, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !accept || !*accept )
|
if ( limit <= 0 || !s || !accept || !*accept )
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
register unsigned int i,j;
|
|
||||||
size_t acc_len = strlen( accept );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
|
||||||
{
|
|
||||||
for ( j = 0; j < acc_len; j++ )
|
|
||||||
{
|
|
||||||
if ( *s == accept[j] )
|
|
||||||
{
|
|
||||||
return( (char *)s );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
|
register unsigned int i,j;
|
||||||
|
size_t acc_len = strlen( accept );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
for ( j = 0; j < acc_len; j++ )
|
||||||
|
{
|
||||||
|
if ( *s == accept[j] )
|
||||||
|
{
|
||||||
|
return( (char *)s );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline char *memstr( register const char *s, const char *n, size_t limit )
|
inline char *memstr( register const char *s, const char *n, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !n )
|
if ( limit <= 0 || !s || !n )
|
||||||
return( 0 );
|
|
||||||
|
|
||||||
if ( !*n )
|
|
||||||
return( (char *)s );
|
|
||||||
|
|
||||||
register unsigned int i,j,k;
|
|
||||||
size_t n_len = strlen( n );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; i++, s++ )
|
|
||||||
{
|
|
||||||
if ( *s != *n )
|
|
||||||
continue;
|
|
||||||
j = 1;
|
|
||||||
k = 1;
|
|
||||||
while ( true )
|
|
||||||
{
|
|
||||||
if ( k >= n_len )
|
|
||||||
return( (char *)s );
|
|
||||||
if ( s[j++] != n[k++] )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
|
if ( !*n )
|
||||||
|
return( (char *)s );
|
||||||
|
|
||||||
|
register unsigned int i,j,k;
|
||||||
|
size_t n_len = strlen( n );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; i++, s++ )
|
||||||
|
{
|
||||||
|
if ( *s != *n )
|
||||||
|
continue;
|
||||||
|
j = 1;
|
||||||
|
k = 1;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
if ( k >= n_len )
|
||||||
|
return( (char *)s );
|
||||||
|
if ( s[j++] != n[k++] )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
inline size_t memspn( register const char *s, const char *accept, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !accept || !*accept )
|
if ( limit <= 0 || !s || !accept || !*accept )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
register unsigned int i,j;
|
register unsigned int i,j;
|
||||||
size_t acc_len = strlen( accept );
|
size_t acc_len = strlen( accept );
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
register bool found = false;
|
||||||
|
for ( j = 0; j < acc_len; j++ )
|
||||||
{
|
{
|
||||||
register bool found = false;
|
if ( *s == accept[j] )
|
||||||
for ( j = 0; j < acc_len; j++ )
|
{
|
||||||
{
|
found = true;
|
||||||
if ( *s == accept[j] )
|
break;
|
||||||
{
|
}
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !found )
|
|
||||||
{
|
|
||||||
return( i );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return( limit );
|
if ( !found )
|
||||||
|
{
|
||||||
|
return( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( limit );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
inline size_t memcspn( register const char *s, const char *reject, size_t limit )
|
||||||
{
|
{
|
||||||
if ( limit <= 0 || !s || !reject )
|
if ( limit <= 0 || !s || !reject )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
|
|
||||||
if ( !*reject )
|
if ( !*reject )
|
||||||
return( limit );
|
|
||||||
|
|
||||||
register unsigned int i,j;
|
|
||||||
size_t rej_len = strlen( reject );
|
|
||||||
|
|
||||||
for ( i = 0; i < limit; s++, i++ )
|
|
||||||
{
|
|
||||||
for ( j = 0; j < rej_len; j++ )
|
|
||||||
{
|
|
||||||
if ( *s == reject[j] )
|
|
||||||
{
|
|
||||||
return( i );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return( limit );
|
return( limit );
|
||||||
|
|
||||||
|
register unsigned int i,j;
|
||||||
|
size_t rej_len = strlen( reject );
|
||||||
|
|
||||||
|
for ( i = 0; i < limit; s++, i++ )
|
||||||
|
{
|
||||||
|
for ( j = 0; j < rej_len; j++ )
|
||||||
|
{
|
||||||
|
if ( *s == reject[j] )
|
||||||
|
{
|
||||||
|
return( i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( limit );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_MEM_UTILS_H
|
#endif // ZM_MEM_UTILS_H
|
||||||
|
|
7402
src/zm_monitor.cpp
7402
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;
|
friend class MonitorStream;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
QUERY=0,
|
QUERY=0,
|
||||||
CAPTURE,
|
CAPTURE,
|
||||||
ANALYSIS
|
ANALYSIS
|
||||||
} Purpose;
|
} Purpose;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
NONE=1,
|
NONE=1,
|
||||||
MONITOR,
|
MONITOR,
|
||||||
MODECT,
|
MODECT,
|
||||||
RECORD,
|
RECORD,
|
||||||
MOCORD,
|
MOCORD,
|
||||||
NODECT
|
NODECT
|
||||||
} Function;
|
} Function;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
ROTATE_0=1,
|
ROTATE_0=1,
|
||||||
ROTATE_90,
|
ROTATE_90,
|
||||||
ROTATE_180,
|
ROTATE_180,
|
||||||
ROTATE_270,
|
ROTATE_270,
|
||||||
FLIP_HORI,
|
FLIP_HORI,
|
||||||
FLIP_VERT
|
FLIP_VERT
|
||||||
} Orientation;
|
} Orientation;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
IDLE,
|
IDLE,
|
||||||
PREALARM,
|
PREALARM,
|
||||||
ALARM,
|
ALARM,
|
||||||
ALERT,
|
ALERT,
|
||||||
TAPE
|
TAPE
|
||||||
} State;
|
} State;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef std::set<Zone *> ZoneSet;
|
typedef std::set<Zone *> ZoneSet;
|
||||||
|
|
||||||
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action;
|
||||||
|
|
||||||
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode;
|
||||||
|
|
||||||
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
/* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t size; /* +0 */
|
uint32_t size; /* +0 */
|
||||||
uint32_t last_write_index; /* +4 */
|
uint32_t last_write_index; /* +4 */
|
||||||
uint32_t last_read_index; /* +8 */
|
uint32_t last_read_index; /* +8 */
|
||||||
uint32_t state; /* +12 */
|
uint32_t state; /* +12 */
|
||||||
uint32_t last_event; /* +16 */
|
uint32_t last_event; /* +16 */
|
||||||
uint32_t action; /* +20 */
|
uint32_t action; /* +20 */
|
||||||
int32_t brightness; /* +24 */
|
int32_t brightness; /* +24 */
|
||||||
int32_t hue; /* +28 */
|
int32_t hue; /* +28 */
|
||||||
int32_t colour; /* +32 */
|
int32_t colour; /* +32 */
|
||||||
int32_t contrast; /* +36 */
|
int32_t contrast; /* +36 */
|
||||||
int32_t alarm_x; /* +40 */
|
int32_t alarm_x; /* +40 */
|
||||||
int32_t alarm_y; /* +44 */
|
int32_t alarm_y; /* +44 */
|
||||||
uint8_t valid; /* +48 */
|
uint8_t valid; /* +48 */
|
||||||
uint8_t active; /* +49 */
|
uint8_t active; /* +49 */
|
||||||
uint8_t signal; /* +50 */
|
uint8_t signal; /* +50 */
|
||||||
uint8_t format; /* +51 */
|
uint8_t format; /* +51 */
|
||||||
uint32_t imagesize; /* +52 */
|
uint32_t imagesize; /* +52 */
|
||||||
uint32_t epadding1; /* +56 */
|
uint32_t epadding1; /* +56 */
|
||||||
uint32_t epadding2; /* +60 */
|
uint32_t epadding2; /* +60 */
|
||||||
/*
|
/*
|
||||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||||
*/
|
*/
|
||||||
union { /* +64 */
|
union { /* +64 */
|
||||||
time_t last_write_time;
|
time_t last_write_time;
|
||||||
uint64_t extrapad1;
|
uint64_t extrapad1;
|
||||||
};
|
};
|
||||||
union { /* +72 */
|
union { /* +72 */
|
||||||
time_t last_read_time;
|
time_t last_read_time;
|
||||||
uint64_t extrapad2;
|
uint64_t extrapad2;
|
||||||
};
|
};
|
||||||
uint8_t control_state[256]; /* +80 */
|
uint8_t control_state[256]; /* +80 */
|
||||||
|
|
||||||
} SharedData;
|
} SharedData;
|
||||||
|
|
||||||
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
|
||||||
|
|
||||||
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
/* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t trigger_state;
|
uint32_t trigger_state;
|
||||||
uint32_t trigger_score;
|
uint32_t trigger_score;
|
||||||
uint32_t padding;
|
uint32_t padding;
|
||||||
char trigger_cause[32];
|
char trigger_cause[32];
|
||||||
char trigger_text[256];
|
char trigger_text[256];
|
||||||
char trigger_showtext[256];
|
char trigger_showtext[256];
|
||||||
} TriggerData;
|
} TriggerData;
|
||||||
|
|
||||||
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
|
||||||
struct Snapshot
|
struct Snapshot
|
||||||
{
|
{
|
||||||
struct timeval *timestamp;
|
struct timeval *timestamp;
|
||||||
Image *image;
|
Image *image;
|
||||||
void* padding;
|
void* padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MonitorLink
|
class MonitorLink
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
char name[64];
|
char name[64];
|
||||||
|
|
||||||
bool connected;
|
bool connected;
|
||||||
time_t last_connect_time;
|
time_t last_connect_time;
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
int map_fd;
|
int map_fd;
|
||||||
char mem_file[PATH_MAX];
|
char mem_file[PATH_MAX];
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
int shm_id;
|
int shm_id;
|
||||||
#endif // ZM_MEM_MAPPED
|
#endif // ZM_MEM_MAPPED
|
||||||
off_t mem_size;
|
off_t mem_size;
|
||||||
unsigned char *mem_ptr;
|
unsigned char *mem_ptr;
|
||||||
|
|
||||||
volatile SharedData *shared_data;
|
volatile SharedData *shared_data;
|
||||||
volatile TriggerData *trigger_data;
|
volatile TriggerData *trigger_data;
|
||||||
|
|
||||||
int last_state;
|
int last_state;
|
||||||
int last_event;
|
int last_event;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MonitorLink( int p_id, const char *p_name );
|
MonitorLink( int p_id, const char *p_name );
|
||||||
~MonitorLink();
|
~MonitorLink();
|
||||||
|
|
||||||
inline int Id() const
|
inline int Id() const
|
||||||
{
|
{
|
||||||
return( id );
|
return( id );
|
||||||
}
|
}
|
||||||
inline const char *Name() const
|
inline const char *Name() const
|
||||||
{
|
{
|
||||||
return( name );
|
return( name );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isConnected() const
|
inline bool isConnected() const
|
||||||
{
|
{
|
||||||
return( connected );
|
return( connected );
|
||||||
}
|
}
|
||||||
inline time_t getLastConnectTime() const
|
inline time_t getLastConnectTime() const
|
||||||
{
|
{
|
||||||
return( last_connect_time );
|
return( last_connect_time );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool connect();
|
bool connect();
|
||||||
bool disconnect();
|
bool disconnect();
|
||||||
|
|
||||||
bool isAlarmed();
|
bool isAlarmed();
|
||||||
bool inAlarm();
|
bool inAlarm();
|
||||||
bool hasAlarmed();
|
bool hasAlarmed();
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// These are read from the DB and thereafter remain unchanged
|
// These are read from the DB and thereafter remain unchanged
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
char name[64];
|
char name[64];
|
||||||
unsigned int server_id;
|
unsigned int server_id;
|
||||||
Function function; // What the monitor is doing
|
Function function; // What the monitor is doing
|
||||||
bool enabled; // Whether the monitor is enabled or asleep
|
bool enabled; // Whether the monitor is enabled or asleep
|
||||||
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
unsigned int width; // Normally the same as the camera, but not if partly rotated
|
||||||
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
unsigned int height; // Normally the same as the camera, but not if partly rotated
|
||||||
bool v4l_multi_buffer;
|
bool v4l_multi_buffer;
|
||||||
unsigned int v4l_captures_per_frame;
|
unsigned int v4l_captures_per_frame;
|
||||||
Orientation orientation; // Whether the image has to be rotated at all
|
Orientation orientation; // Whether the image has to be rotated at all
|
||||||
unsigned int deinterlacing;
|
unsigned int deinterlacing;
|
||||||
int brightness; // The statically saved brightness of the camera
|
int brightness; // The statically saved brightness of the camera
|
||||||
int contrast; // The statically saved contrast of the camera
|
int contrast; // The statically saved contrast of the camera
|
||||||
int hue; // The statically saved hue of the camera
|
int hue; // The statically saved hue of the camera
|
||||||
int colour; // The statically saved colour of the camera
|
int colour; // The statically saved colour of the camera
|
||||||
char event_prefix[64]; // The prefix applied to event names as they are created
|
char event_prefix[64]; // The prefix applied to event names as they are created
|
||||||
char label_format[64]; // The format of the timestamp on the images
|
char label_format[64]; // The format of the timestamp on the images
|
||||||
Coord label_coord; // The coordinates of the timestamp on the images
|
Coord label_coord; // The coordinates of the timestamp on the images
|
||||||
int label_size; // Size of the timestamp on the images
|
int label_size; // Size of the timestamp on the images
|
||||||
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count
|
||||||
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate,
|
||||||
// value is pre_event_count + alarm_frame_count - 1
|
// value is pre_event_count + alarm_frame_count - 1
|
||||||
int warmup_count; // How many images to process before looking for events
|
int warmup_count; // How many images to process before looking for events
|
||||||
int pre_event_count; // How many images to hold and prepend to an alarm event
|
int pre_event_count; // How many images to hold and prepend to an alarm event
|
||||||
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
|
||||||
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
|
||||||
int section_length; // How long events should last in continuous modes
|
int section_length; // How long events should last in continuous modes
|
||||||
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
|
||||||
int frame_skip; // How many frames to skip in continuous modes
|
int frame_skip; // How many frames to skip in continuous modes
|
||||||
int motion_frame_skip; // How many frames to skip in motion detection
|
int motion_frame_skip; // How many frames to skip in motion detection
|
||||||
double analysis_fps; // Target framerate for video analysis
|
double analysis_fps; // Target framerate for video analysis
|
||||||
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
|
||||||
int capture_delay; // How long we wait between capture frames
|
int capture_delay; // How long we wait between capture frames
|
||||||
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
|
||||||
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
int alarm_frame_count; // How many alarm frames are required before an event is triggered
|
||||||
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
|
||||||
int ref_blend_perc; // Percentage of new image going into reference image.
|
int ref_blend_perc; // Percentage of new image going into reference image.
|
||||||
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
|
||||||
bool track_motion; // Whether this monitor tries to track detected motion
|
bool track_motion; // Whether this monitor tries to track detected motion
|
||||||
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
|
||||||
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
bool embed_exif; // Whether to embed Exif data into each image frame or not
|
||||||
|
|
||||||
double fps;
|
double fps;
|
||||||
Image delta_image;
|
Image delta_image;
|
||||||
Image ref_image;
|
Image ref_image;
|
||||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||||
Image write_image; // Used when creating snapshot images
|
Image write_image; // Used when creating snapshot images
|
||||||
|
|
||||||
Purpose purpose; // What this monitor has been created to do
|
Purpose purpose; // What this monitor has been created to do
|
||||||
int event_count;
|
int event_count;
|
||||||
int image_count;
|
int image_count;
|
||||||
int ready_count;
|
int ready_count;
|
||||||
int first_alarm_count;
|
int first_alarm_count;
|
||||||
int last_alarm_count;
|
int last_alarm_count;
|
||||||
int buffer_count;
|
int buffer_count;
|
||||||
int prealarm_count;
|
int prealarm_count;
|
||||||
State state;
|
State state;
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
time_t last_fps_time;
|
time_t last_fps_time;
|
||||||
time_t auto_resume_time;
|
time_t auto_resume_time;
|
||||||
unsigned int last_motion_score;
|
unsigned int last_motion_score;
|
||||||
|
|
||||||
EventCloseMode event_close_mode;
|
EventCloseMode event_close_mode;
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
int map_fd;
|
int map_fd;
|
||||||
char mem_file[PATH_MAX];
|
char mem_file[PATH_MAX];
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
int shm_id;
|
int shm_id;
|
||||||
#endif // ZM_MEM_MAPPED
|
#endif // ZM_MEM_MAPPED
|
||||||
off_t mem_size;
|
off_t mem_size;
|
||||||
unsigned char *mem_ptr;
|
unsigned char *mem_ptr;
|
||||||
|
|
||||||
SharedData *shared_data;
|
SharedData *shared_data;
|
||||||
TriggerData *trigger_data;
|
TriggerData *trigger_data;
|
||||||
|
|
||||||
Snapshot *image_buffer;
|
Snapshot *image_buffer;
|
||||||
Snapshot next_buffer; /* Used by four field deinterlacing */
|
Snapshot next_buffer; /* Used by four field deinterlacing */
|
||||||
Snapshot *pre_event_buffer;
|
Snapshot *pre_event_buffer;
|
||||||
|
|
||||||
Camera *camera;
|
Camera *camera;
|
||||||
|
|
||||||
Event *event;
|
Event *event;
|
||||||
|
|
||||||
int n_zones;
|
int n_zones;
|
||||||
Zone **zones;
|
Zone **zones;
|
||||||
|
|
||||||
struct timeval **timestamps;
|
struct timeval **timestamps;
|
||||||
Image **images;
|
Image **images;
|
||||||
|
|
||||||
const unsigned char *privacy_bitmask;
|
const unsigned char *privacy_bitmask;
|
||||||
|
|
||||||
int n_linked_monitors;
|
int n_linked_monitors;
|
||||||
MonitorLink **linked_monitors;
|
MonitorLink **linked_monitors;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||||
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
|
||||||
Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 );
|
||||||
~Monitor();
|
~Monitor();
|
||||||
|
|
||||||
void AddZones( int p_n_zones, Zone *p_zones[] );
|
void AddZones( int p_n_zones, Zone *p_zones[] );
|
||||||
void AddPrivacyBitmask( Zone *p_zones[] );
|
void AddPrivacyBitmask( Zone *p_zones[] );
|
||||||
|
|
||||||
bool connect();
|
bool connect();
|
||||||
inline int ShmValid() const
|
inline int ShmValid() const
|
||||||
{
|
{
|
||||||
return( shared_data->valid );
|
return( shared_data->valid );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int Id() const
|
inline int Id() const
|
||||||
{
|
{
|
||||||
return( id );
|
return( id );
|
||||||
}
|
}
|
||||||
inline const char *Name() const
|
inline const char *Name() const
|
||||||
{
|
{
|
||||||
return( name );
|
return( name );
|
||||||
}
|
}
|
||||||
inline Function GetFunction() const
|
inline Function GetFunction() const
|
||||||
{
|
{
|
||||||
return( function );
|
return( function );
|
||||||
}
|
}
|
||||||
inline bool Enabled()
|
inline bool Enabled()
|
||||||
{
|
{
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( enabled );
|
return( enabled );
|
||||||
}
|
}
|
||||||
inline const char *EventPrefix() const
|
inline const char *EventPrefix() const
|
||||||
{
|
{
|
||||||
return( event_prefix );
|
return( event_prefix );
|
||||||
}
|
}
|
||||||
inline bool Ready()
|
inline bool Ready()
|
||||||
{
|
{
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( image_count > ready_count );
|
return( image_count > ready_count );
|
||||||
}
|
}
|
||||||
inline bool Active()
|
inline bool Active()
|
||||||
{
|
{
|
||||||
if ( function <= MONITOR )
|
if ( function <= MONITOR )
|
||||||
return( false );
|
return( false );
|
||||||
return( enabled && shared_data->active );
|
return( enabled && shared_data->active );
|
||||||
}
|
}
|
||||||
inline bool Exif()
|
inline bool Exif()
|
||||||
{
|
{
|
||||||
return( embed_exif );
|
return( embed_exif );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Width() const { return( width ); }
|
unsigned int Width() const { return( width ); }
|
||||||
unsigned int Height() const { return( height ); }
|
unsigned int Height() const { return( height ); }
|
||||||
unsigned int Colours() const { return( camera->Colours() ); }
|
unsigned int Colours() const { return( camera->Colours() ); }
|
||||||
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); }
|
||||||
|
|
||||||
|
|
||||||
State GetState() const;
|
State GetState() const;
|
||||||
int GetImage( int index=-1, int scale=100 );
|
int GetImage( int index=-1, int scale=100 );
|
||||||
struct timeval GetTimestamp( int index=-1 ) const;
|
struct timeval GetTimestamp( int index=-1 ) const;
|
||||||
void UpdateAdaptiveSkip();
|
void UpdateAdaptiveSkip();
|
||||||
useconds_t GetAnalysisRate();
|
useconds_t GetAnalysisRate();
|
||||||
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); }
|
||||||
int GetCaptureDelay() const { return( capture_delay ); }
|
int GetCaptureDelay() const { return( capture_delay ); }
|
||||||
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); }
|
||||||
unsigned int GetLastReadIndex() const;
|
unsigned int GetLastReadIndex() const;
|
||||||
unsigned int GetLastWriteIndex() const;
|
unsigned int GetLastWriteIndex() const;
|
||||||
unsigned int GetLastEvent() const;
|
unsigned int GetLastEvent() const;
|
||||||
double GetFPS() const;
|
double GetFPS() const;
|
||||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||||
void ForceAlarmOff();
|
void ForceAlarmOff();
|
||||||
void CancelForced();
|
void CancelForced();
|
||||||
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); }
|
||||||
|
|
||||||
void actionReload();
|
void actionReload();
|
||||||
void actionEnable();
|
void actionEnable();
|
||||||
void actionDisable();
|
void actionDisable();
|
||||||
void actionSuspend();
|
void actionSuspend();
|
||||||
void actionResume();
|
void actionResume();
|
||||||
|
|
||||||
int actionBrightness( int p_brightness=-1 );
|
int actionBrightness( int p_brightness=-1 );
|
||||||
int actionHue( int p_hue=-1 );
|
int actionHue( int p_hue=-1 );
|
||||||
int actionColour( int p_colour=-1 );
|
int actionColour( int p_colour=-1 );
|
||||||
int actionContrast( int p_contrast=-1 );
|
int actionContrast( int p_contrast=-1 );
|
||||||
|
|
||||||
inline int PrimeCapture()
|
inline int PrimeCapture()
|
||||||
{
|
{
|
||||||
return( camera->PrimeCapture() );
|
return( camera->PrimeCapture() );
|
||||||
}
|
}
|
||||||
inline int PreCapture()
|
inline int PreCapture()
|
||||||
{
|
{
|
||||||
return( camera->PreCapture() );
|
return( camera->PreCapture() );
|
||||||
}
|
}
|
||||||
int Capture();
|
int Capture();
|
||||||
int PostCapture()
|
int PostCapture()
|
||||||
{
|
{
|
||||||
return( camera->PostCapture() );
|
return( camera->PostCapture() );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet );
|
||||||
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
|
// DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info.
|
||||||
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
//unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet );
|
||||||
bool CheckSignal( const Image *image );
|
bool CheckSignal( const Image *image );
|
||||||
bool Analyse();
|
bool Analyse();
|
||||||
void DumpImage( Image *dump_image ) const;
|
void DumpImage( Image *dump_image ) const;
|
||||||
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const;
|
||||||
bool closeEvent();
|
bool closeEvent();
|
||||||
|
|
||||||
void Reload();
|
void Reload();
|
||||||
void ReloadZones();
|
void ReloadZones();
|
||||||
void ReloadLinkedMonitors( const char * );
|
void ReloadLinkedMonitors( const char * );
|
||||||
|
|
||||||
bool DumpSettings( char *output, bool verbose );
|
bool DumpSettings( char *output, bool verbose );
|
||||||
void DumpZoneImage( const char *zone_string=0 );
|
void DumpZoneImage( const char *zone_string=0 );
|
||||||
|
|
||||||
#if ZM_HAS_V4L
|
#if ZM_HAS_V4L
|
||||||
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose );
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose );
|
||||||
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||||
#if HAVE_LIBAVFORMAT
|
#if HAVE_LIBAVFORMAT
|
||||||
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
|
||||||
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
|
||||||
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
|
||||||
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||||
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
//void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||||
void SingleImage( int scale=100 );
|
void SingleImage( int scale=100 );
|
||||||
void SingleImageRaw( int scale=100 );
|
void SingleImageRaw( int scale=100 );
|
||||||
void SingleImageZip( int scale=100 );
|
void SingleImageZip( int scale=100 );
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
//void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -448,51 +448,51 @@ public:
|
||||||
class MonitorStream : public StreamBase
|
class MonitorStream : public StreamBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
typedef struct SwapImage {
|
typedef struct SwapImage {
|
||||||
bool valid;
|
bool valid;
|
||||||
struct timeval timestamp;
|
struct timeval timestamp;
|
||||||
char file_name[PATH_MAX];
|
char file_name[PATH_MAX];
|
||||||
} SwapImage;
|
} SwapImage;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SwapImage *temp_image_buffer;
|
SwapImage *temp_image_buffer;
|
||||||
int temp_image_buffer_count;
|
int temp_image_buffer_count;
|
||||||
int temp_read_index;
|
int temp_read_index;
|
||||||
int temp_write_index;
|
int temp_write_index;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
time_t ttl;
|
time_t ttl;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int playback_buffer;
|
int playback_buffer;
|
||||||
bool delayed;
|
bool delayed;
|
||||||
|
|
||||||
int frame_count;
|
int frame_count;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool checkSwapPath( const char *path, bool create_path );
|
bool checkSwapPath( const char *path, bool create_path );
|
||||||
|
|
||||||
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
bool sendFrame( const char *filepath, struct timeval *timestamp );
|
||||||
bool sendFrame( Image *image, struct timeval *timestamp );
|
bool sendFrame( Image *image, struct timeval *timestamp );
|
||||||
void processCommand( const CmdMsg *msg );
|
void processCommand( const CmdMsg *msg );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 )
|
MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void setStreamBuffer( int p_playback_buffer )
|
void setStreamBuffer( int p_playback_buffer )
|
||||||
{
|
{
|
||||||
playback_buffer = p_playback_buffer;
|
playback_buffer = p_playback_buffer;
|
||||||
}
|
}
|
||||||
void setStreamTTL( time_t p_ttl )
|
void setStreamTTL( time_t p_ttl )
|
||||||
{
|
{
|
||||||
ttl = p_ttl;
|
ttl = p_ttl;
|
||||||
}
|
}
|
||||||
bool setStreamStart( int monitor_id )
|
bool setStreamStart( int monitor_id )
|
||||||
{
|
{
|
||||||
return loadMonitor( monitor_id );
|
return loadMonitor( monitor_id );
|
||||||
}
|
}
|
||||||
void runStream();
|
void runStream();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_MONITOR_H
|
#endif // ZM_MONITOR_H
|
||||||
|
|
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
|
class VideoStream
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct MimeData
|
struct MimeData
|
||||||
{
|
{
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *mime_type;
|
const char *mime_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool initialised;
|
static bool initialised;
|
||||||
static struct MimeData mime_data[];
|
static struct MimeData mime_data[];
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char *codec_and_format;
|
char *codec_and_format;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *codec_name;
|
const char *codec_name;
|
||||||
enum _AVPIXELFORMAT pf;
|
enum _AVPIXELFORMAT pf;
|
||||||
AVOutputFormat *of;
|
AVOutputFormat *of;
|
||||||
AVFormatContext *ofc;
|
AVFormatContext *ofc;
|
||||||
AVStream *ost;
|
AVStream *ost;
|
||||||
AVCodec *codec;
|
AVCodec *codec;
|
||||||
AVFrame *opicture;
|
AVFrame *opicture;
|
||||||
AVFrame *tmp_opicture;
|
AVFrame *tmp_opicture;
|
||||||
uint8_t *video_outbuf;
|
uint8_t *video_outbuf;
|
||||||
int video_outbuf_size;
|
int video_outbuf_size;
|
||||||
double last_pts;
|
double last_pts;
|
||||||
|
|
||||||
pthread_t streaming_thread;
|
pthread_t streaming_thread;
|
||||||
bool do_streaming;
|
bool do_streaming;
|
||||||
uint8_t *buffer_copy;
|
uint8_t *buffer_copy;
|
||||||
bool add_timestamp;
|
bool add_timestamp;
|
||||||
unsigned int timestamp;
|
unsigned int timestamp;
|
||||||
pthread_mutex_t *buffer_copy_lock;
|
pthread_mutex_t *buffer_copy_lock;
|
||||||
int buffer_copy_size;
|
int buffer_copy_size;
|
||||||
int buffer_copy_used;
|
int buffer_copy_used;
|
||||||
AVPacket** packet_buffers;
|
AVPacket** packet_buffers;
|
||||||
int packet_index;
|
int packet_index;
|
||||||
int SendPacket(AVPacket *packet);
|
int SendPacket(AVPacket *packet);
|
||||||
static void* StreamingThreadCallback(void *ctx);
|
static void* StreamingThreadCallback(void *ctx);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
|
|
||||||
void SetupFormat( );
|
void SetupFormat( );
|
||||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||||
void SetParameters();
|
void SetParameters();
|
||||||
void ActuallyOpenStream();
|
void ActuallyOpenStream();
|
||||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
|
||||||
~VideoStream();
|
~VideoStream();
|
||||||
const char *MimeType() const;
|
const char *MimeType() const;
|
||||||
void OpenStream();
|
void OpenStream();
|
||||||
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
140
src/zm_poly.cpp
140
src/zm_poly.cpp
|
@ -28,95 +28,95 @@
|
||||||
|
|
||||||
void Polygon::calcArea()
|
void Polygon::calcArea()
|
||||||
{
|
{
|
||||||
double float_area = 0.0L;
|
double float_area = 0.0L;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L;
|
||||||
float_area += trap_area;
|
float_area += trap_area;
|
||||||
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
//printf( "%.2f (%.2f)\n", float_area, trap_area );
|
||||||
}
|
}
|
||||||
area = (int)round(fabs(float_area));
|
area = (int)round(fabs(float_area));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Polygon::calcCentre()
|
void Polygon::calcCentre()
|
||||||
{
|
{
|
||||||
if ( !area && n_coords )
|
if ( !area && n_coords )
|
||||||
calcArea();
|
calcArea();
|
||||||
double float_x = 0.0L, float_y = 0.0L;
|
double float_x = 0.0L, float_y = 0.0L;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2)));
|
||||||
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2)));
|
||||||
}
|
}
|
||||||
float_x /= (6*area);
|
float_x /= (6*area);
|
||||||
float_y /= (6*area);
|
float_y /= (6*area);
|
||||||
//printf( "%.2f,%.2f\n", float_x, float_y );
|
//printf( "%.2f,%.2f\n", float_x, float_y );
|
||||||
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
centre = Coord( (int)round(float_x), (int)round(float_y) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords )
|
||||||
{
|
{
|
||||||
coords = new Coord[n_coords];
|
coords = new Coord[n_coords];
|
||||||
|
|
||||||
int min_x = -1;
|
int min_x = -1;
|
||||||
int max_x = -1;
|
int max_x = -1;
|
||||||
int min_y = -1;
|
int min_y = -1;
|
||||||
int max_y = -1;
|
int max_y = -1;
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_coords[i];
|
coords[i] = p_coords[i];
|
||||||
if ( min_x == -1 || coords[i].X() < min_x )
|
if ( min_x == -1 || coords[i].X() < min_x )
|
||||||
min_x = coords[i].X();
|
min_x = coords[i].X();
|
||||||
if ( max_x == -1 || coords[i].X() > max_x )
|
if ( max_x == -1 || coords[i].X() > max_x )
|
||||||
max_x = coords[i].X();
|
max_x = coords[i].X();
|
||||||
if ( min_y == -1 || coords[i].Y() < min_y )
|
if ( min_y == -1 || coords[i].Y() < min_y )
|
||||||
min_y = coords[i].Y();
|
min_y = coords[i].Y();
|
||||||
if ( max_y == -1 || coords[i].Y() > max_y )
|
if ( max_y == -1 || coords[i].Y() > max_y )
|
||||||
max_y = coords[i].Y();
|
max_y = coords[i].Y();
|
||||||
}
|
}
|
||||||
extent = Box( min_x, min_y, max_x, max_y );
|
extent = Box( min_x, min_y, max_x, max_y );
|
||||||
calcArea();
|
calcArea();
|
||||||
calcCentre();
|
calcCentre();
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre )
|
||||||
{
|
{
|
||||||
coords = new Coord[n_coords];
|
coords = new Coord[n_coords];
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_polygon.coords[i];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
Polygon &Polygon::operator=( const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
if ( n_coords < p_polygon.n_coords )
|
if ( n_coords < p_polygon.n_coords )
|
||||||
{
|
{
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
coords = new Coord[p_polygon.n_coords];
|
coords = new Coord[p_polygon.n_coords];
|
||||||
}
|
}
|
||||||
n_coords = p_polygon.n_coords;
|
n_coords = p_polygon.n_coords;
|
||||||
for( int i = 0; i < n_coords; i++ )
|
for( int i = 0; i < n_coords; i++ )
|
||||||
{
|
{
|
||||||
coords[i] = p_polygon.coords[i];
|
coords[i] = p_polygon.coords[i];
|
||||||
}
|
}
|
||||||
extent = p_polygon.extent;
|
extent = p_polygon.extent;
|
||||||
area = p_polygon.area;
|
area = p_polygon.area;
|
||||||
centre = p_polygon.centre;
|
centre = p_polygon.centre;
|
||||||
return( *this );
|
return( *this );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Polygon::isInside( const Coord &coord ) const
|
bool Polygon::isInside( const Coord &coord ) const
|
||||||
{
|
{
|
||||||
bool inside = false;
|
bool inside = false;
|
||||||
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ )
|
||||||
{
|
{
|
||||||
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) )
|
||||||
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
|| ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y())))
|
||||||
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
&& (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X()))
|
||||||
{
|
{
|
||||||
inside = !inside;
|
inside = !inside;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( inside );
|
return( inside );
|
||||||
}
|
}
|
||||||
|
|
148
src/zm_poly.h
148
src/zm_poly.h
|
@ -33,93 +33,93 @@
|
||||||
class Polygon
|
class Polygon
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct Edge
|
struct Edge
|
||||||
{
|
{
|
||||||
int min_y;
|
int min_y;
|
||||||
int max_y;
|
int max_y;
|
||||||
double min_x;
|
double min_x;
|
||||||
double _1_m;
|
double _1_m;
|
||||||
|
|
||||||
static int CompareYX( const void *p1, const void *p2 )
|
static int CompareYX( const void *p1, const void *p2 )
|
||||||
{
|
{
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
if ( e1->min_y == e2->min_y )
|
if ( e1->min_y == e2->min_y )
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
else
|
else
|
||||||
return( int(e1->min_y - e2->min_y) );
|
return( int(e1->min_y - e2->min_y) );
|
||||||
}
|
}
|
||||||
static int CompareX( const void *p1, const void *p2 )
|
static int CompareX( const void *p1, const void *p2 )
|
||||||
{
|
{
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Slice
|
struct Slice
|
||||||
{
|
{
|
||||||
int min_x;
|
int min_x;
|
||||||
int max_x;
|
int max_x;
|
||||||
int n_edges;
|
int n_edges;
|
||||||
int *edges;
|
int *edges;
|
||||||
|
|
||||||
Slice()
|
Slice()
|
||||||
{
|
{
|
||||||
n_edges = 0;
|
n_edges = 0;
|
||||||
edges = 0;
|
edges = 0;
|
||||||
}
|
}
|
||||||
~Slice()
|
~Slice()
|
||||||
{
|
{
|
||||||
delete edges;
|
delete edges;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int n_coords;
|
int n_coords;
|
||||||
Coord *coords;
|
Coord *coords;
|
||||||
Box extent;
|
Box extent;
|
||||||
int area;
|
int area;
|
||||||
Coord centre;
|
Coord centre;
|
||||||
Edge *edges;
|
Edge *edges;
|
||||||
Slice *slices;
|
Slice *slices;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialiseEdges();
|
void initialiseEdges();
|
||||||
void calcArea();
|
void calcArea();
|
||||||
void calcCentre();
|
void calcCentre();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
Polygon( int p_n_coords, const Coord *p_coords );
|
Polygon( int p_n_coords, const Coord *p_coords );
|
||||||
Polygon( const Polygon &p_polygon );
|
Polygon( const Polygon &p_polygon );
|
||||||
~Polygon()
|
~Polygon()
|
||||||
{
|
{
|
||||||
delete[] coords;
|
delete[] coords;
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon &operator=( const Polygon &p_polygon );
|
Polygon &operator=( const Polygon &p_polygon );
|
||||||
|
|
||||||
inline int getNumCoords() const { return( n_coords ); }
|
inline int getNumCoords() const { return( n_coords ); }
|
||||||
inline const Coord &getCoord( int index ) const
|
inline const Coord &getCoord( int index ) const
|
||||||
{
|
{
|
||||||
return( coords[index] );
|
return( coords[index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const Box &Extent() const { return( extent ); }
|
inline const Box &Extent() const { return( extent ); }
|
||||||
inline int LoX() const { return( extent.LoX() ); }
|
inline int LoX() const { return( extent.LoX() ); }
|
||||||
inline int HiX() const { return( extent.HiX() ); }
|
inline int HiX() const { return( extent.HiX() ); }
|
||||||
inline int LoY() const { return( extent.LoY() ); }
|
inline int LoY() const { return( extent.LoY() ); }
|
||||||
inline int HiY() const { return( extent.HiY() ); }
|
inline int HiY() const { return( extent.HiY() ); }
|
||||||
inline int Width() const { return( extent.Width() ); }
|
inline int Width() const { return( extent.Width() ); }
|
||||||
inline int Height() const { return( extent.Height() ); }
|
inline int Height() const { return( extent.Height() ); }
|
||||||
|
|
||||||
inline int Area() const { return( area ); }
|
inline int Area() const { return( area ); }
|
||||||
inline const Coord &Centre() const
|
inline const Coord &Centre() const
|
||||||
{
|
{
|
||||||
return( centre );
|
return( centre );
|
||||||
}
|
}
|
||||||
bool isInside( const Coord &coord ) const;
|
bool isInside( const Coord &coord ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_POLY_H
|
#endif // ZM_POLY_H
|
||||||
|
|
|
@ -26,99 +26,99 @@
|
||||||
|
|
||||||
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 )
|
||||||
{
|
{
|
||||||
const char *errstr;
|
const char *errstr;
|
||||||
int erroffset = 0;
|
int erroffset = 0;
|
||||||
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) )
|
||||||
{
|
{
|
||||||
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset );
|
||||||
}
|
}
|
||||||
|
|
||||||
regextra = pcre_study( regex, 0, &errstr );
|
regextra = pcre_study( regex, 0, &errstr );
|
||||||
if ( errstr )
|
if ( errstr )
|
||||||
{
|
{
|
||||||
Panic( "pcre_study(%s): %s", pattern, errstr );
|
Panic( "pcre_study(%s): %s", pattern, errstr );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (ok = (bool)regex) )
|
if ( (ok = (bool)regex) )
|
||||||
{
|
{
|
||||||
match_vectors = new int[3*max_matches];
|
match_vectors = new int[3*max_matches];
|
||||||
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches );
|
||||||
match_buffers = new char *[max_matches];
|
match_buffers = new char *[max_matches];
|
||||||
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
memset( match_buffers, 0, sizeof(*match_buffers)*max_matches );
|
||||||
match_lengths = new int[max_matches];
|
match_lengths = new int[max_matches];
|
||||||
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
memset( match_lengths, 0, sizeof(*match_lengths)*max_matches );
|
||||||
match_valid = new bool[max_matches];
|
match_valid = new bool[max_matches];
|
||||||
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
memset( match_valid, 0, sizeof(*match_valid)*max_matches );
|
||||||
}
|
}
|
||||||
n_matches = 0;
|
n_matches = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpr::~RegExpr()
|
RegExpr::~RegExpr()
|
||||||
{
|
{
|
||||||
for ( int i = 0; i < max_matches; i++ )
|
for ( int i = 0; i < max_matches; i++ )
|
||||||
{
|
{
|
||||||
if ( match_buffers[i] )
|
if ( match_buffers[i] )
|
||||||
{
|
{
|
||||||
delete[] match_buffers[i];
|
delete[] match_buffers[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete[] match_valid;
|
delete[] match_valid;
|
||||||
delete[] match_lengths;
|
delete[] match_lengths;
|
||||||
delete[] match_buffers;
|
delete[] match_buffers;
|
||||||
delete[] match_vectors;
|
delete[] match_vectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
int RegExpr::Match( const char *subject_string, int subject_length, int flags )
|
||||||
{
|
{
|
||||||
match_string = subject_string;
|
match_string = subject_string;
|
||||||
|
|
||||||
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches );
|
||||||
|
|
||||||
if ( n_matches <= 0 )
|
if ( n_matches <= 0 )
|
||||||
{
|
{
|
||||||
if ( n_matches < PCRE_ERROR_NOMATCH )
|
if ( n_matches < PCRE_ERROR_NOMATCH )
|
||||||
{
|
{
|
||||||
Error( "Error %d executing regular expression", n_matches );
|
Error( "Error %d executing regular expression", n_matches );
|
||||||
}
|
}
|
||||||
return( n_matches = 0 );
|
return( n_matches = 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( int i = 0; i < max_matches; i++ )
|
for( int i = 0; i < max_matches; i++ )
|
||||||
{
|
{
|
||||||
match_valid[i] = false;
|
match_valid[i] = false;
|
||||||
}
|
}
|
||||||
return( n_matches );
|
return( n_matches );
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *RegExpr::MatchString( int match_index ) const
|
const char *RegExpr::MatchString( int match_index ) const
|
||||||
{
|
{
|
||||||
if ( match_index > n_matches )
|
if ( match_index > n_matches )
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
if ( !match_valid[match_index] )
|
if ( !match_valid[match_index] )
|
||||||
{
|
{
|
||||||
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index];
|
||||||
if ( match_lengths[match_index] < (match_len+1) )
|
if ( match_lengths[match_index] < (match_len+1) )
|
||||||
{
|
{
|
||||||
delete[] match_buffers[match_index];
|
delete[] match_buffers[match_index];
|
||||||
match_buffers[match_index] = new char[match_len+1];
|
match_buffers[match_index] = new char[match_len+1];
|
||||||
match_lengths[match_index] = match_len+1;
|
match_lengths[match_index] = match_len+1;
|
||||||
}
|
}
|
||||||
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len );
|
||||||
match_buffers[match_index][match_len] = '\0';
|
match_buffers[match_index][match_len] = '\0';
|
||||||
match_valid[match_index] = true;
|
match_valid[match_index] = true;
|
||||||
}
|
}
|
||||||
return( match_buffers[match_index] );
|
return( match_buffers[match_index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegExpr::MatchLength( int match_index ) const
|
int RegExpr::MatchLength( int match_index ) const
|
||||||
{
|
{
|
||||||
if ( match_index > n_matches )
|
if ( match_index > n_matches )
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBPCRE
|
#endif // HAVE_LIBPCRE
|
||||||
|
|
|
@ -35,29 +35,29 @@
|
||||||
class RegExpr
|
class RegExpr
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
pcre *regex;
|
pcre *regex;
|
||||||
pcre_extra *regextra;
|
pcre_extra *regextra;
|
||||||
int max_matches;
|
int max_matches;
|
||||||
int *match_vectors;
|
int *match_vectors;
|
||||||
mutable char **match_buffers;
|
mutable char **match_buffers;
|
||||||
int *match_lengths;
|
int *match_lengths;
|
||||||
bool *match_valid;
|
bool *match_valid;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const char *match_string;
|
const char *match_string;
|
||||||
int n_matches;
|
int n_matches;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool ok;
|
bool ok;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 );
|
||||||
~RegExpr();
|
~RegExpr();
|
||||||
bool Ok() const { return( ok ); }
|
bool Ok() const { return( ok ); }
|
||||||
int MatchCount() const { return( n_matches ); }
|
int MatchCount() const { return( n_matches ); }
|
||||||
int Match( const char *subject_string, int subject_length, int flags=0 );
|
int Match( const char *subject_string, int subject_length, int flags=0 );
|
||||||
const char *MatchString( int match_index ) const;
|
const char *MatchString( int match_index ) const;
|
||||||
int MatchLength( int match_index ) const;
|
int MatchLength( int match_index ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBPCRE
|
#endif // HAVE_LIBPCRE
|
||||||
|
|
|
@ -22,66 +22,66 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
|
|
||||||
RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||||
protocol( p_protocol ),
|
protocol( p_protocol ),
|
||||||
host( p_host ),
|
host( p_host ),
|
||||||
port( p_port ),
|
port( p_port ),
|
||||||
path( p_path ),
|
path( p_path ),
|
||||||
hp( 0 )
|
hp( 0 )
|
||||||
{
|
{
|
||||||
if ( path[0] != '/' )
|
if ( path[0] != '/' )
|
||||||
path = '/'+path;
|
path = '/'+path;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteCamera::~RemoteCamera()
|
RemoteCamera::~RemoteCamera()
|
||||||
{
|
{
|
||||||
if(hp != NULL) {
|
if(hp != NULL) {
|
||||||
freeaddrinfo(hp);
|
freeaddrinfo(hp);
|
||||||
hp = NULL;
|
hp = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteCamera::Initialise()
|
void RemoteCamera::Initialise()
|
||||||
{
|
{
|
||||||
if( protocol.empty() )
|
if( protocol.empty() )
|
||||||
Fatal( "No protocol specified for remote camera" );
|
Fatal( "No protocol specified for remote camera" );
|
||||||
|
|
||||||
if( host.empty() )
|
if( host.empty() )
|
||||||
Fatal( "No host specified for remote camera" );
|
Fatal( "No host specified for remote camera" );
|
||||||
|
|
||||||
if( port.empty() )
|
if( port.empty() )
|
||||||
Fatal( "No port specified for remote camera" );
|
Fatal( "No port specified for remote camera" );
|
||||||
|
|
||||||
//if( path.empty() )
|
//if( path.empty() )
|
||||||
//Fatal( "No path specified for remote camera" );
|
//Fatal( "No path specified for remote camera" );
|
||||||
|
|
||||||
// Cache as much as we can to speed things up
|
// Cache as much as we can to speed things up
|
||||||
std::string::size_type authIndex = host.rfind( '@' );
|
std::string::size_type authIndex = host.rfind( '@' );
|
||||||
|
|
||||||
if ( authIndex != std::string::npos )
|
if ( authIndex != std::string::npos )
|
||||||
{
|
{
|
||||||
auth = host.substr( 0, authIndex );
|
auth = host.substr( 0, authIndex );
|
||||||
host.erase( 0, authIndex+1 );
|
host.erase( 0, authIndex+1 );
|
||||||
auth64 = base64Encode( auth );
|
auth64 = base64Encode( auth );
|
||||||
|
|
||||||
authIndex = auth.rfind( ':' );
|
authIndex = auth.rfind( ':' );
|
||||||
username = auth.substr(0,authIndex);
|
username = auth.substr(0,authIndex);
|
||||||
password = auth.substr( authIndex+1, auth.length() );
|
password = auth.substr( authIndex+1, auth.length() );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mNeedAuth = false;
|
mNeedAuth = false;
|
||||||
mAuthenticator = new zm::Authenticator(username,password);
|
mAuthenticator = new zm::Authenticator(username,password);
|
||||||
|
|
||||||
struct addrinfo hints;
|
struct addrinfo hints;
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
||||||
if ( ret != 0 )
|
if ( ret != 0 )
|
||||||
{
|
{
|
||||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,44 +35,44 @@
|
||||||
class RemoteCamera : public Camera
|
class RemoteCamera : public Camera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string port;
|
std::string port;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string auth;
|
std::string auth;
|
||||||
std::string username;
|
std::string username;
|
||||||
std::string password;
|
std::string password;
|
||||||
std::string auth64;
|
std::string auth64;
|
||||||
|
|
||||||
// Reworked authentication system
|
// Reworked authentication system
|
||||||
// First try without authentication, even if we have a username and password
|
// First try without authentication, even if we have a username and password
|
||||||
// on receiving a 401 response, select authentication method (basic or digest)
|
// on receiving a 401 response, select authentication method (basic or digest)
|
||||||
// fill required fields and set needAuth
|
// fill required fields and set needAuth
|
||||||
// subsequent requests can set the required authentication header.
|
// subsequent requests can set the required authentication header.
|
||||||
bool mNeedAuth;
|
bool mNeedAuth;
|
||||||
zm::Authenticator* mAuthenticator;
|
zm::Authenticator* mAuthenticator;
|
||||||
protected:
|
protected:
|
||||||
struct addrinfo *hp;
|
struct addrinfo *hp;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
virtual ~RemoteCamera();
|
virtual ~RemoteCamera();
|
||||||
|
|
||||||
const std::string &Protocol() const { return( protocol ); }
|
const std::string &Protocol() const { return( protocol ); }
|
||||||
const std::string &Host() const { return( host ); }
|
const std::string &Host() const { return( host ); }
|
||||||
const std::string &Port() const { return( port ); }
|
const std::string &Port() const { return( port ); }
|
||||||
const std::string &Path() const { return( path ); }
|
const std::string &Path() const { return( path ); }
|
||||||
const std::string &Auth() const { return( auth ); }
|
const std::string &Auth() const { return( auth ); }
|
||||||
const std::string &Username() const { return( username ); }
|
const std::string &Username() const { return( username ); }
|
||||||
const std::string &Password() const { return( password ); }
|
const std::string &Password() const { return( password ); }
|
||||||
|
|
||||||
virtual void Initialise();
|
virtual void Initialise();
|
||||||
virtual void Terminate() = 0;
|
virtual void Terminate() = 0;
|
||||||
virtual int Connect() = 0;
|
virtual int Connect() = 0;
|
||||||
virtual int Disconnect() = 0;
|
virtual int Disconnect() = 0;
|
||||||
virtual int PreCapture() = 0;
|
virtual int PreCapture() = 0;
|
||||||
virtual int Capture( Image &image ) = 0;
|
virtual int Capture( Image &image ) = 0;
|
||||||
virtual int PostCapture() = 0;
|
virtual int PostCapture() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_REMOTE_CAMERA_H
|
#endif // ZM_REMOTE_CAMERA_H
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -33,31 +33,31 @@
|
||||||
class RemoteCameraHttp : public RemoteCamera
|
class RemoteCameraHttp : public RemoteCamera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string request;
|
std::string request;
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
//struct hostent *hp;
|
//struct hostent *hp;
|
||||||
//struct sockaddr_in sa;
|
//struct sockaddr_in sa;
|
||||||
int sd;
|
int sd;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
|
enum { SINGLE_IMAGE, MULTI_IMAGE } mode;
|
||||||
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
|
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
|
||||||
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
||||||
enum { SIMPLE, REGEXP } method;
|
enum { SIMPLE, REGEXP } method;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~RemoteCameraHttp();
|
~RemoteCameraHttp();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate() { Disconnect(); }
|
void Terminate() { Disconnect(); }
|
||||||
int Connect();
|
int Connect();
|
||||||
int Disconnect();
|
int Disconnect();
|
||||||
int SendRequest();
|
int SendRequest();
|
||||||
int ReadData( Buffer &buffer, int bytes_expected=0 );
|
int ReadData( Buffer &buffer, int bytes_expected=0 );
|
||||||
int GetResponse();
|
int GetResponse();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
||||||
|
|
|
@ -29,343 +29,343 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
|
||||||
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ),
|
||||||
rtsp_describe( p_rtsp_describe ),
|
rtsp_describe( p_rtsp_describe ),
|
||||||
rtspThread( 0 )
|
rtspThread( 0 )
|
||||||
|
|
||||||
{
|
{
|
||||||
if ( p_method == "rtpUni" )
|
if ( p_method == "rtpUni" )
|
||||||
method = RtspThread::RTP_UNICAST;
|
method = RtspThread::RTP_UNICAST;
|
||||||
else if ( p_method == "rtpMulti" )
|
else if ( p_method == "rtpMulti" )
|
||||||
method = RtspThread::RTP_MULTICAST;
|
method = RtspThread::RTP_MULTICAST;
|
||||||
else if ( p_method == "rtpRtsp" )
|
else if ( p_method == "rtpRtsp" )
|
||||||
method = RtspThread::RTP_RTSP;
|
method = RtspThread::RTP_RTSP;
|
||||||
else if ( p_method == "rtpRtspHttp" )
|
else if ( p_method == "rtpRtspHttp" )
|
||||||
method = RtspThread::RTP_RTSP_HTTP;
|
method = RtspThread::RTP_RTSP_HTTP;
|
||||||
else
|
else
|
||||||
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id );
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mCodecContext = NULL;
|
mCodecContext = NULL;
|
||||||
mCodec = NULL;
|
mCodec = NULL;
|
||||||
mRawFrame = NULL;
|
mRawFrame = NULL;
|
||||||
mFrame = NULL;
|
mFrame = NULL;
|
||||||
frameCount = 0;
|
frameCount = 0;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||||
if(colours == ZM_COLOUR_RGB32) {
|
if(colours == ZM_COLOUR_RGB32) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||||
} else if(colours == ZM_COLOUR_RGB24) {
|
} else if(colours == ZM_COLOUR_RGB24) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
} else if(colours == ZM_COLOUR_GRAY8) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||||
} else {
|
} else {
|
||||||
Panic("Unexpected colours: %d",colours);
|
Panic("Unexpected colours: %d",colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteCameraRtsp::~RemoteCameraRtsp()
|
RemoteCameraRtsp::~RemoteCameraRtsp()
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
av_frame_free( &mFrame );
|
av_frame_free( &mFrame );
|
||||||
av_frame_free( &mRawFrame );
|
av_frame_free( &mRawFrame );
|
||||||
#else
|
#else
|
||||||
av_freep( &mFrame );
|
av_freep( &mFrame );
|
||||||
av_freep( &mRawFrame );
|
av_freep( &mRawFrame );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if ( mConvertContext )
|
if ( mConvertContext )
|
||||||
{
|
{
|
||||||
sws_freeContext( mConvertContext );
|
sws_freeContext( mConvertContext );
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( mCodecContext )
|
if ( mCodecContext )
|
||||||
{
|
{
|
||||||
avcodec_close( mCodecContext );
|
avcodec_close( mCodecContext );
|
||||||
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( capture )
|
if ( capture )
|
||||||
{
|
{
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteCameraRtsp::Initialise()
|
void RemoteCameraRtsp::Initialise()
|
||||||
{
|
{
|
||||||
RemoteCamera::Initialise();
|
RemoteCamera::Initialise();
|
||||||
|
|
||||||
int max_size = width*height*colours;
|
int max_size = width*height*colours;
|
||||||
|
|
||||||
buffer.size( max_size );
|
buffer.size( max_size );
|
||||||
|
|
||||||
if ( logDebugging() )
|
if ( logDebugging() )
|
||||||
av_log_set_level( AV_LOG_DEBUG );
|
av_log_set_level( AV_LOG_DEBUG );
|
||||||
else
|
else
|
||||||
av_log_set_level( AV_LOG_QUIET );
|
av_log_set_level( AV_LOG_QUIET );
|
||||||
|
|
||||||
av_register_all();
|
av_register_all();
|
||||||
|
|
||||||
Connect();
|
Connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteCameraRtsp::Terminate()
|
void RemoteCameraRtsp::Terminate()
|
||||||
{
|
{
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Connect()
|
int RemoteCameraRtsp::Connect()
|
||||||
{
|
{
|
||||||
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe );
|
||||||
|
|
||||||
rtspThread->start();
|
rtspThread->start();
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Disconnect()
|
int RemoteCameraRtsp::Disconnect()
|
||||||
{
|
{
|
||||||
if ( rtspThread )
|
if ( rtspThread )
|
||||||
{
|
{
|
||||||
rtspThread->stop();
|
rtspThread->stop();
|
||||||
rtspThread->join();
|
rtspThread->join();
|
||||||
delete rtspThread;
|
delete rtspThread;
|
||||||
rtspThread = 0;
|
rtspThread = 0;
|
||||||
}
|
}
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::PrimeCapture()
|
int RemoteCameraRtsp::PrimeCapture()
|
||||||
{
|
{
|
||||||
Debug( 2, "Waiting for sources" );
|
Debug( 2, "Waiting for sources" );
|
||||||
for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ )
|
for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ )
|
||||||
{
|
{
|
||||||
usleep( 100000 );
|
usleep( 100000 );
|
||||||
}
|
}
|
||||||
if ( !rtspThread->hasSources() )
|
if ( !rtspThread->hasSources() )
|
||||||
Fatal( "No RTSP sources" );
|
Fatal( "No RTSP sources" );
|
||||||
|
|
||||||
Debug( 2, "Got sources" );
|
Debug( 2, "Got sources" );
|
||||||
|
|
||||||
mFormatContext = rtspThread->getFormatContext();
|
mFormatContext = rtspThread->getFormatContext();
|
||||||
|
|
||||||
// Find first video stream present
|
// Find first video stream present
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
|
||||||
#else
|
#else
|
||||||
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
mVideoStreamId = i;
|
mVideoStreamId = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( mVideoStreamId == -1 )
|
if ( mVideoStreamId == -1 )
|
||||||
Fatal( "Unable to locate video stream" );
|
Fatal( "Unable to locate video stream" );
|
||||||
|
|
||||||
// Get a pointer to the codec context for the video stream
|
// Get a pointer to the codec context for the video stream
|
||||||
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
|
||||||
|
|
||||||
// Find the decoder for the video stream
|
// Find the decoder for the video stream
|
||||||
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
mCodec = avcodec_find_decoder( mCodecContext->codec_id );
|
||||||
if ( mCodec == NULL )
|
if ( mCodec == NULL )
|
||||||
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id );
|
||||||
|
|
||||||
// Open codec
|
// Open codec
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
|
||||||
#else
|
#else
|
||||||
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
|
||||||
#endif
|
#endif
|
||||||
Panic( "Can't open codec" );
|
Panic( "Can't open codec" );
|
||||||
|
|
||||||
// Allocate space for the native video frame
|
// Allocate space for the native video frame
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
mRawFrame = av_frame_alloc();
|
mRawFrame = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
mRawFrame = avcodec_alloc_frame();
|
mRawFrame = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Allocate space for the converted video frame
|
// Allocate space for the converted video frame
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
mFrame = av_frame_alloc();
|
mFrame = av_frame_alloc();
|
||||||
#else
|
#else
|
||||||
mFrame = avcodec_alloc_frame();
|
mFrame = avcodec_alloc_frame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(mRawFrame == NULL || mFrame == NULL)
|
if(mRawFrame == NULL || mFrame == NULL)
|
||||||
Fatal( "Unable to allocate frame(s)");
|
Fatal( "Unable to allocate frame(s)");
|
||||||
|
|
||||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||||
if( (unsigned int)pSize != imagesize) {
|
if( (unsigned int)pSize != imagesize) {
|
||||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
|
||||||
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
if(!sws_isSupportedOutput(imagePixFormat)) {
|
||||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::PreCapture()
|
int RemoteCameraRtsp::PreCapture()
|
||||||
{
|
{
|
||||||
if ( !rtspThread->isRunning() )
|
if ( !rtspThread->isRunning() )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if ( !rtspThread->hasSources() )
|
if ( !rtspThread->hasSources() )
|
||||||
{
|
{
|
||||||
Error( "Cannot precapture, no RTP sources" );
|
Error( "Cannot precapture, no RTP sources" );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::Capture( Image &image )
|
int RemoteCameraRtsp::Capture( Image &image )
|
||||||
{
|
{
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
uint8_t* directbuffer;
|
uint8_t* directbuffer;
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
|
|
||||||
/* Request a writeable buffer of the target image */
|
/* Request a writeable buffer of the target image */
|
||||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
if(directbuffer == NULL) {
|
if(directbuffer == NULL) {
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( true )
|
while ( true )
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
if ( !rtspThread->isRunning() )
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
if ( rtspThread->getFrame( buffer ) )
|
||||||
{
|
{
|
||||||
buffer.clear();
|
Debug( 3, "Read frame %d bytes", buffer.size() );
|
||||||
if ( !rtspThread->isRunning() )
|
Debug( 4, "Address %p", buffer.head() );
|
||||||
return (-1);
|
Hexdump( 4, buffer.head(), 16 );
|
||||||
|
|
||||||
if ( rtspThread->getFrame( buffer ) )
|
if ( !buffer.size() )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if(mCodecContext->codec_id == AV_CODEC_ID_H264)
|
||||||
|
{
|
||||||
|
// SPS and PPS frames should be saved and appended to IDR frames
|
||||||
|
int nalType = (buffer.head()[3] & 0x1f);
|
||||||
|
|
||||||
|
// SPS
|
||||||
|
if(nalType == 7)
|
||||||
{
|
{
|
||||||
Debug( 3, "Read frame %d bytes", buffer.size() );
|
lastSps = buffer;
|
||||||
Debug( 4, "Address %p", buffer.head() );
|
continue;
|
||||||
Hexdump( 4, buffer.head(), 16 );
|
}
|
||||||
|
// PPS
|
||||||
|
else if(nalType == 8)
|
||||||
|
{
|
||||||
|
lastPps = buffer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// IDR
|
||||||
|
else if(nalType == 5)
|
||||||
|
{
|
||||||
|
buffer += lastSps;
|
||||||
|
buffer += lastPps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( !buffer.size() )
|
av_init_packet( &packet );
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
if(mCodecContext->codec_id == AV_CODEC_ID_H264)
|
while ( !frameComplete && buffer.size() > 0 )
|
||||||
{
|
{
|
||||||
// SPS and PPS frames should be saved and appended to IDR frames
|
packet.data = buffer.head();
|
||||||
int nalType = (buffer.head()[3] & 0x1f);
|
packet.size = buffer.size();
|
||||||
|
|
||||||
// SPS
|
|
||||||
if(nalType == 7)
|
|
||||||
{
|
|
||||||
lastSps = buffer;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// PPS
|
|
||||||
else if(nalType == 8)
|
|
||||||
{
|
|
||||||
lastPps = buffer;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// IDR
|
|
||||||
else if(nalType == 5)
|
|
||||||
{
|
|
||||||
buffer += lastSps;
|
|
||||||
buffer += lastPps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
av_init_packet( &packet );
|
|
||||||
|
|
||||||
while ( !frameComplete && buffer.size() > 0 )
|
|
||||||
{
|
|
||||||
packet.data = buffer.head();
|
|
||||||
packet.size = buffer.size();
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
|
||||||
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet );
|
||||||
#else
|
#else
|
||||||
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size );
|
||||||
#endif
|
#endif
|
||||||
if ( len < 0 )
|
if ( len < 0 )
|
||||||
{
|
{
|
||||||
Error( "Error while decoding frame %d", frameCount );
|
Error( "Error while decoding frame %d", frameCount );
|
||||||
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() );
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() );
|
||||||
//if ( buffer.size() < 400 )
|
//if ( buffer.size() < 400 )
|
||||||
//Hexdump( 0, buffer.head(), buffer.size() );
|
//Hexdump( 0, buffer.head(), buffer.size() );
|
||||||
|
|
||||||
buffer -= len;
|
buffer -= len;
|
||||||
|
|
||||||
}
|
|
||||||
if ( frameComplete ) {
|
|
||||||
|
|
||||||
Debug( 3, "Got frame %d", frameCount );
|
|
||||||
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
if(mConvertContext == NULL) {
|
|
||||||
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
|
||||||
|
|
||||||
if(mConvertContext == NULL)
|
|
||||||
Fatal( "Unable to create conversion context");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
|
||||||
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
|
||||||
#else // HAVE_LIBSWSCALE
|
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
|
||||||
#endif // HAVE_LIBSWSCALE
|
|
||||||
|
|
||||||
frameCount++;
|
|
||||||
|
|
||||||
} /* frame complete */
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
|
||||||
av_packet_unref( &packet);
|
|
||||||
#else
|
|
||||||
av_free_packet( &packet );
|
|
||||||
#endif
|
|
||||||
} /* getFrame() */
|
|
||||||
|
|
||||||
if(frameComplete)
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return (0) ;
|
if ( frameComplete ) {
|
||||||
|
|
||||||
|
Debug( 3, "Got frame %d", frameCount );
|
||||||
|
|
||||||
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||||
|
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
if(mConvertContext == NULL) {
|
||||||
|
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
|
||||||
|
|
||||||
|
if(mConvertContext == NULL)
|
||||||
|
Fatal( "Unable to create conversion context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
|
||||||
|
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
|
||||||
|
#else // HAVE_LIBSWSCALE
|
||||||
|
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
|
||||||
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
|
frameCount++;
|
||||||
|
|
||||||
|
} /* frame complete */
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
|
||||||
|
av_packet_unref( &packet);
|
||||||
|
#else
|
||||||
|
av_free_packet( &packet );
|
||||||
|
#endif
|
||||||
|
} /* getFrame() */
|
||||||
|
|
||||||
|
if(frameComplete)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
}
|
||||||
|
return (0) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RemoteCameraRtsp::PostCapture()
|
int RemoteCameraRtsp::PostCapture()
|
||||||
{
|
{
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -35,50 +35,50 @@
|
||||||
class RemoteCameraRtsp : public RemoteCamera
|
class RemoteCameraRtsp : public RemoteCamera
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct sockaddr_in rtsp_sa;
|
struct sockaddr_in rtsp_sa;
|
||||||
struct sockaddr_in rtcp_sa;
|
struct sockaddr_in rtcp_sa;
|
||||||
int rtsp_sd;
|
int rtsp_sd;
|
||||||
int rtp_sd;
|
int rtp_sd;
|
||||||
int rtcp_sd;
|
int rtcp_sd;
|
||||||
bool rtsp_describe;
|
bool rtsp_describe;
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Buffer lastSps;
|
Buffer lastSps;
|
||||||
Buffer lastPps;
|
Buffer lastPps;
|
||||||
|
|
||||||
RtspThread::RtspMethod method;
|
RtspThread::RtspMethod method;
|
||||||
|
|
||||||
RtspThread *rtspThread;
|
RtspThread *rtspThread;
|
||||||
|
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
#if HAVE_LIBAVFORMAT
|
#if HAVE_LIBAVFORMAT
|
||||||
AVFormatContext *mFormatContext;
|
AVFormatContext *mFormatContext;
|
||||||
int mVideoStreamId;
|
int mVideoStreamId;
|
||||||
AVCodecContext *mCodecContext;
|
AVCodecContext *mCodecContext;
|
||||||
AVCodec *mCodec;
|
AVCodec *mCodec;
|
||||||
AVFrame *mRawFrame;
|
AVFrame *mRawFrame;
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext;
|
struct SwsContext *mConvertContext;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
|
||||||
~RemoteCameraRtsp();
|
~RemoteCameraRtsp();
|
||||||
|
|
||||||
void Initialise();
|
void Initialise();
|
||||||
void Terminate();
|
void Terminate();
|
||||||
int Connect();
|
int Connect();
|
||||||
int Disconnect();
|
int Disconnect();
|
||||||
|
|
||||||
int PrimeCapture();
|
int PrimeCapture();
|
||||||
int PreCapture();
|
int PreCapture();
|
||||||
int Capture( Image &image );
|
int Capture( Image &image );
|
||||||
int PostCapture();
|
int PostCapture();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
180
src/zm_rgb.h
180
src/zm_rgb.h
|
@ -20,80 +20,80 @@
|
||||||
#ifndef ZM_RGB_H
|
#ifndef ZM_RGB_H
|
||||||
#define ZM_RGB_H
|
#define ZM_RGB_H
|
||||||
|
|
||||||
typedef uint32_t Rgb; // RGB colour type
|
typedef uint32_t Rgb; // RGB colour type
|
||||||
|
|
||||||
#define WHITE 0xff
|
#define WHITE 0xff
|
||||||
#define WHITE_R 0xff
|
#define WHITE_R 0xff
|
||||||
#define WHITE_G 0xff
|
#define WHITE_G 0xff
|
||||||
#define WHITE_B 0xff
|
#define WHITE_B 0xff
|
||||||
|
|
||||||
#define BLACK 0x00
|
#define BLACK 0x00
|
||||||
#define BLACK_R 0x00
|
#define BLACK_R 0x00
|
||||||
#define BLACK_G 0x00
|
#define BLACK_G 0x00
|
||||||
#define BLACK_B 0x00
|
#define BLACK_B 0x00
|
||||||
|
|
||||||
#define RGB_WHITE (0x00ffffff)
|
#define RGB_WHITE (0x00ffffff)
|
||||||
#define RGB_BLACK (0x00000000)
|
#define RGB_BLACK (0x00000000)
|
||||||
#define RGB_RED (0x000000ff)
|
#define RGB_RED (0x000000ff)
|
||||||
#define RGB_GREEN (0x0000ff00)
|
#define RGB_GREEN (0x0000ff00)
|
||||||
#define RGB_BLUE (0x00ff0000)
|
#define RGB_BLUE (0x00ff0000)
|
||||||
#define RGB_ORANGE (0x0000a5ff)
|
#define RGB_ORANGE (0x0000a5ff)
|
||||||
#define RGB_PURPLE (0x00800080)
|
#define RGB_PURPLE (0x00800080)
|
||||||
#define RGB_TRANSPARENT (0x01000000)
|
#define RGB_TRANSPARENT (0x01000000)
|
||||||
|
|
||||||
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff)
|
||||||
|
|
||||||
/* RGB or RGBA macros */
|
/* RGB or RGBA macros */
|
||||||
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff)
|
||||||
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff)
|
||||||
#define RED_VAL_RGBA(v) ((v)&0xff)
|
#define RED_VAL_RGBA(v) ((v)&0xff)
|
||||||
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff)
|
||||||
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr))
|
||||||
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3))
|
||||||
|
|
||||||
/* BGR or BGRA */
|
/* BGR or BGRA */
|
||||||
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
#define RED_VAL_BGRA(v) (((v)>>16)&0xff)
|
||||||
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff)
|
||||||
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
#define BLUE_VAL_BGRA(v) ((v)&0xff)
|
||||||
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff)
|
||||||
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr))
|
||||||
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3))
|
||||||
|
|
||||||
/* ARGB */
|
/* ARGB */
|
||||||
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff)
|
||||||
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff)
|
||||||
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
#define RED_VAL_ARGB(v) (((v)>>8)&0xff)
|
||||||
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
#define ALPHA_VAL_ARGB(v) ((v)&0xff)
|
||||||
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3))
|
||||||
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr))
|
||||||
|
|
||||||
/* ABGR */
|
/* ABGR */
|
||||||
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff)
|
||||||
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff)
|
||||||
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
#define RED_VAL_ABGR(v) (((v)>>24)&0xff)
|
||||||
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
#define ALPHA_VAL_ABGR(v) ((v)&0xff)
|
||||||
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3))
|
||||||
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2))
|
||||||
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1))
|
||||||
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr))
|
||||||
|
|
||||||
|
|
||||||
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff)
|
||||||
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00)
|
||||||
|
|
||||||
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
/* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */
|
||||||
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
/* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */
|
||||||
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
/* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */
|
||||||
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||||
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||||
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3)
|
||||||
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3)
|
||||||
|
|
||||||
/* ZM colours */
|
/* ZM colours */
|
||||||
#define ZM_COLOUR_RGB32 4
|
#define ZM_COLOUR_RGB32 4
|
||||||
|
@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type
|
||||||
|
|
||||||
/* A macro to use default subpixel order for a specified colour. */
|
/* A macro to use default subpixel order for a specified colour. */
|
||||||
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
/* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */
|
||||||
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1)
|
||||||
|
|
||||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||||
Rgb result;
|
Rgb result;
|
||||||
|
|
||||||
switch(p_subpixorder) {
|
switch(p_subpixorder) {
|
||||||
|
|
||||||
case ZM_SUBPIX_ORDER_BGR:
|
case ZM_SUBPIX_ORDER_BGR:
|
||||||
case ZM_SUBPIX_ORDER_BGRA:
|
case ZM_SUBPIX_ORDER_BGRA:
|
||||||
{
|
{
|
||||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_SUBPIX_ORDER_ARGB:
|
case ZM_SUBPIX_ORDER_ARGB:
|
||||||
{
|
{
|
||||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_SUBPIX_ORDER_ABGR:
|
case ZM_SUBPIX_ORDER_ABGR:
|
||||||
{
|
{
|
||||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
/* Grayscale */
|
/* Grayscale */
|
||||||
case ZM_SUBPIX_ORDER_NONE:
|
case ZM_SUBPIX_ORDER_NONE:
|
||||||
result = p_col & 0xff;
|
result = p_col & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return p_col;
|
return p_col;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_RGB_H
|
#endif // ZM_RGB_H
|
||||||
|
|
|
@ -34,343 +34,343 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
||||||
|
|
||||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
const RtcpPacket *rtcpPacket;
|
const RtcpPacket *rtcpPacket;
|
||||||
rtcpPacket = (RtcpPacket *)packet;
|
rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int consumed = 0;
|
int consumed = 0;
|
||||||
|
|
||||||
//printf( "C: " );
|
//printf( "C: " );
|
||||||
//for ( int i = 0; i < packetLen; i++ )
|
//for ( int i = 0; i < packetLen; i++ )
|
||||||
//printf( "%02x ", (unsigned char)packet[i] );
|
//printf( "%02x ", (unsigned char)packet[i] );
|
||||||
//printf( "\n" );
|
//printf( "\n" );
|
||||||
int ver = rtcpPacket->header.version;
|
int ver = rtcpPacket->header.version;
|
||||||
int count = rtcpPacket->header.count;
|
int count = rtcpPacket->header.count;
|
||||||
int pt = rtcpPacket->header.pt;
|
int pt = rtcpPacket->header.pt;
|
||||||
int len = ntohs(rtcpPacket->header.lenN);
|
int len = ntohs(rtcpPacket->header.lenN);
|
||||||
|
|
||||||
Debug( 5, "RTCP Ver: %d", ver );
|
Debug( 5, "RTCP Ver: %d", ver );
|
||||||
Debug( 5, "RTCP Count: %d", count );
|
Debug( 5, "RTCP Count: %d", count );
|
||||||
Debug( 5, "RTCP Pt: %d", pt );
|
Debug( 5, "RTCP Pt: %d", pt );
|
||||||
Debug( 5, "RTCP len: %d", len );
|
Debug( 5, "RTCP len: %d", len );
|
||||||
|
|
||||||
switch( pt )
|
switch( pt )
|
||||||
|
{
|
||||||
|
case RTCP_SR :
|
||||||
{
|
{
|
||||||
case RTCP_SR :
|
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||||
{
|
|
||||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
|
||||||
|
|
||||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||||
if ( mRtpSource.getSsrc() )
|
if ( mRtpSource.getSsrc() )
|
||||||
{
|
{
|
||||||
if ( ssrc != mRtpSource.getSsrc() )
|
if ( ssrc != mRtpSource.getSsrc() )
|
||||||
{
|
{
|
||||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( ssrc )
|
else if ( ssrc )
|
||||||
{
|
{
|
||||||
mRtpSource.setSsrc( ssrc );
|
mRtpSource.setSsrc( ssrc );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( len > 1 )
|
if ( len > 1 )
|
||||||
{
|
{
|
||||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||||
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
//printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||||
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
//printf( "Pkts:$sendpkts, Octs:$sendocts\n" );
|
||||||
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN);
|
||||||
|
|
||||||
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case RTCP_SDES :
|
|
||||||
{
|
|
||||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
|
||||||
while ( contentLen )
|
|
||||||
{
|
|
||||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
|
||||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
|
||||||
|
|
||||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
|
||||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
|
||||||
{
|
|
||||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
|
||||||
for ( int i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
|
||||||
Debug( 5, "RTCP Item length %d", item->len );
|
|
||||||
switch( item->type )
|
|
||||||
{
|
|
||||||
case RTCP_SDES_CNAME :
|
|
||||||
{
|
|
||||||
std::string cname( item->data, item->len );
|
|
||||||
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_SDES_END :
|
|
||||||
case RTCP_SDES_NAME :
|
|
||||||
case RTCP_SDES_EMAIL :
|
|
||||||
case RTCP_SDES_PHONE :
|
|
||||||
case RTCP_SDES_LOC :
|
|
||||||
case RTCP_SDES_TOOL :
|
|
||||||
case RTCP_SDES_NOTE :
|
|
||||||
case RTCP_SDES_PRIV :
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
|
||||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
|
||||||
Debug( 5, "RTCP PL:%d", paddedLen );
|
|
||||||
sdesPtr += paddedLen;
|
|
||||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_BYE :
|
|
||||||
{
|
|
||||||
Debug( 5, "RTCP Got BYE" );
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_APP :
|
|
||||||
{
|
|
||||||
// Ignoring as per RFC 3550
|
|
||||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RTCP_RR :
|
|
||||||
{
|
|
||||||
Error( "Received RTCP_RR packet." );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
default :
|
|
||||||
{
|
|
||||||
// Ignore unknown packet types. Some cameras do this by design.
|
|
||||||
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
consumed = sizeof(uint32_t)*(len+1);
|
case RTCP_SDES :
|
||||||
return( consumed );
|
{
|
||||||
|
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||||
|
while ( contentLen )
|
||||||
|
{
|
||||||
|
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||||
|
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||||
|
|
||||||
|
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||||
|
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||||
|
{
|
||||||
|
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||||
|
for ( int i = 0; i < count; i++ )
|
||||||
|
{
|
||||||
|
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||||
|
Debug( 5, "RTCP Item length %d", item->len );
|
||||||
|
switch( item->type )
|
||||||
|
{
|
||||||
|
case RTCP_SDES_CNAME :
|
||||||
|
{
|
||||||
|
std::string cname( item->data, item->len );
|
||||||
|
Debug( 5, "RTCP Got CNAME %s", cname.c_str() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_SDES_END :
|
||||||
|
case RTCP_SDES_NAME :
|
||||||
|
case RTCP_SDES_EMAIL :
|
||||||
|
case RTCP_SDES_PHONE :
|
||||||
|
case RTCP_SDES_LOC :
|
||||||
|
case RTCP_SDES_TOOL :
|
||||||
|
case RTCP_SDES_NOTE :
|
||||||
|
case RTCP_SDES_PRIV :
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||||
|
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||||
|
Debug( 5, "RTCP PL:%d", paddedLen );
|
||||||
|
sdesPtr += paddedLen;
|
||||||
|
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_BYE :
|
||||||
|
{
|
||||||
|
Debug( 5, "RTCP Got BYE" );
|
||||||
|
mStop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_APP :
|
||||||
|
{
|
||||||
|
// Ignoring as per RFC 3550
|
||||||
|
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RTCP_RR :
|
||||||
|
{
|
||||||
|
Error( "Received RTCP_RR packet." );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
default :
|
||||||
|
{
|
||||||
|
// Ignore unknown packet types. Some cameras do this by design.
|
||||||
|
Debug( 5, "Received unexpected packet type %d, ignoring", pt );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consumed = sizeof(uint32_t)*(len+1);
|
||||||
|
return( consumed );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_RR;
|
rtcpPacket->header.pt = RTCP_RR;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
mRtpSource.updateRtcpStats();
|
mRtpSource.updateRtcpStats();
|
||||||
|
|
||||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||||
|
|
||||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||||
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets();
|
||||||
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction();
|
||||||
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq());
|
||||||
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter());
|
||||||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
const std::string &cname = mRtpSource.getCname();
|
const std::string &cname = mRtpSource.getCname();
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size();
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_SDES;
|
rtcpPacket->header.pt = RTCP_SDES;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1);
|
||||||
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME;
|
||||||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
||||||
{
|
{
|
||||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||||
|
|
||||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||||
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
int wordLen = ((byteLen-1)/sizeof(uint32_t))+1;
|
||||||
|
|
||||||
rtcpPacket->header.version = RTP_VERSION;
|
rtcpPacket->header.version = RTP_VERSION;
|
||||||
rtcpPacket->header.p = 0;
|
rtcpPacket->header.p = 0;
|
||||||
rtcpPacket->header.pt = RTCP_BYE;
|
rtcpPacket->header.pt = RTCP_BYE;
|
||||||
rtcpPacket->header.count = 1;
|
rtcpPacket->header.count = 1;
|
||||||
rtcpPacket->header.lenN = htons(wordLen-1);
|
rtcpPacket->header.lenN = htons(wordLen-1);
|
||||||
|
|
||||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||||
|
|
||||||
return( wordLen*sizeof(uint32_t) );
|
return( wordLen*sizeof(uint32_t) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
||||||
{
|
{
|
||||||
unsigned char *bufferPtr = buffer;
|
unsigned char *bufferPtr = buffer;
|
||||||
|
|
||||||
// u_int32 len; /* length of compound RTCP packet in words */
|
// u_int32 len; /* length of compound RTCP packet in words */
|
||||||
// rtcp_t *r; /* RTCP header */
|
// rtcp_t *r; /* RTCP header */
|
||||||
// rtcp_t *end; /* end of compound RTCP packet */
|
// rtcp_t *end; /* end of compound RTCP packet */
|
||||||
|
|
||||||
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
// if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
|
||||||
// /* something wrong with packet format */
|
// /* something wrong with packet format */
|
||||||
// }
|
// }
|
||||||
// end = (rtcp_t *)((u_int32 *)r + len);
|
// end = (rtcp_t *)((u_int32 *)r + len);
|
||||||
|
|
||||||
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
// do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1);
|
||||||
// while (r < end && r->common.version == 2);
|
// while (r < end && r->common.version == 2);
|
||||||
|
|
||||||
// if (r != end) {
|
// if (r != end) {
|
||||||
// /* something wrong with packet format */
|
// /* something wrong with packet format */
|
||||||
// }
|
// }
|
||||||
|
|
||||||
while ( nBytes > 0 )
|
while ( nBytes > 0 )
|
||||||
{
|
{
|
||||||
int consumed = recvPacket( bufferPtr, nBytes );
|
int consumed = recvPacket( bufferPtr, nBytes );
|
||||||
if ( consumed <= 0 )
|
if ( consumed <= 0 )
|
||||||
break;
|
break;
|
||||||
bufferPtr += consumed;
|
bufferPtr += consumed;
|
||||||
nBytes -= consumed;
|
nBytes -= consumed;
|
||||||
}
|
}
|
||||||
return( nBytes );
|
return( nBytes );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpCtrlThread::run()
|
int RtpCtrlThread::run()
|
||||||
{
|
{
|
||||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||||
SockAddrInet localAddr, remoteAddr;
|
SockAddrInet localAddr, remoteAddr;
|
||||||
|
|
||||||
bool sendReports;
|
bool sendReports;
|
||||||
UdpInetSocket rtpCtrlServer;
|
UdpInetSocket rtpCtrlServer;
|
||||||
if ( mRtpSource.getLocalHost() != "" )
|
if ( mRtpSource.getLocalHost() != "" )
|
||||||
|
{
|
||||||
|
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||||
|
Fatal( "Failed to bind RTCP server" );
|
||||||
|
sendReports = false;
|
||||||
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.bind( localAddr ) )
|
||||||
|
Fatal( "Failed to bind RTCP server" );
|
||||||
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||||
|
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
||||||
|
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
||||||
|
Fatal( "Failed to connect RTCP server" );
|
||||||
|
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
||||||
|
sendReports = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||||
|
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||||
|
Select select( 10 );
|
||||||
|
select.addReader( &rtpCtrlServer );
|
||||||
|
|
||||||
|
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||||
|
|
||||||
|
time_t last_receive = time(NULL);
|
||||||
|
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||||
|
|
||||||
|
while ( !mStop && select.wait() >= 0 ) {
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
Select::CommsList readable = select.getReadable();
|
||||||
|
if ( readable.size() == 0 )
|
||||||
{
|
{
|
||||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" );
|
if ( ! timeout ) {
|
||||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||||
Fatal( "Failed to bind RTCP server" );
|
ssize_t nBytes;
|
||||||
sendReports = false;
|
unsigned char *bufferPtr = buffer;
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
|
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
|
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||||
|
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||||
|
Error( "Unable to send: %s", strerror( errno ) );
|
||||||
|
timeout = true;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
//Error( "RTCP timed out" );
|
||||||
|
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
||||||
|
continue;
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
timeout = false;
|
||||||
|
last_receive = time(NULL);
|
||||||
}
|
}
|
||||||
else
|
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||||
{
|
{
|
||||||
localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" );
|
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||||
if ( !rtpCtrlServer.bind( localAddr ) )
|
{
|
||||||
Fatal( "Failed to bind RTCP server" );
|
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||||
remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" );
|
|
||||||
if ( !rtpCtrlServer.connect( remoteAddr ) )
|
|
||||||
Fatal( "Failed to connect RTCP server" );
|
|
||||||
Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() );
|
|
||||||
sendReports = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
if ( nBytes )
|
||||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
|
||||||
Select select( 10 );
|
|
||||||
select.addReader( &rtpCtrlServer );
|
|
||||||
|
|
||||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
|
||||||
|
|
||||||
time_t last_receive = time(NULL);
|
|
||||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
|
||||||
|
|
||||||
while ( !mStop && select.wait() >= 0 ) {
|
|
||||||
|
|
||||||
time_t now = time(NULL);
|
|
||||||
Select::CommsList readable = select.getReadable();
|
|
||||||
if ( readable.size() == 0 )
|
|
||||||
{
|
{
|
||||||
if ( ! timeout ) {
|
recvPackets( buffer, nBytes );
|
||||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
|
||||||
ssize_t nBytes;
|
|
||||||
unsigned char *bufferPtr = buffer;
|
|
||||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
|
||||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
|
||||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
|
||||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
|
||||||
Error( "Unable to send: %s", strerror( errno ) );
|
|
||||||
timeout = true;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
//Error( "RTCP timed out" );
|
|
||||||
Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) );
|
|
||||||
continue;
|
|
||||||
//break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
timeout = false;
|
|
||||||
last_receive = time(NULL);
|
|
||||||
}
|
|
||||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
|
||||||
{
|
|
||||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
|
||||||
{
|
|
||||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
|
||||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
|
||||||
|
|
||||||
if ( nBytes )
|
if ( sendReports )
|
||||||
{
|
{
|
||||||
recvPackets( buffer, nBytes );
|
unsigned char *bufferPtr = buffer;
|
||||||
|
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
if ( sendReports )
|
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||||
{
|
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
||||||
unsigned char *bufferPtr = buffer;
|
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
||||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
Error( "Unable to send: %s", strerror( errno ) );
|
||||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
||||||
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() );
|
}
|
||||||
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
|
} else {
|
||||||
Error( "Unable to send: %s", strerror( errno ) );
|
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
||||||
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
|
mStop = true;
|
||||||
}
|
break;
|
||||||
} else {
|
|
||||||
// Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs.
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Panic( "Barfed" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Panic( "Barfed" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rtpCtrlServer.close();
|
}
|
||||||
mRtspThread.stop();
|
rtpCtrlServer.close();
|
||||||
return( 0 );
|
mRtspThread.stop();
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include "zm_thread.h"
|
#include "zm_thread.h"
|
||||||
|
|
||||||
// Defined in ffmpeg rtp.h
|
// Defined in ffmpeg rtp.h
|
||||||
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
//#define RTP_MAX_SDES 255 // maximum text length for SDES
|
||||||
|
|
||||||
// Big-endian mask for version, padding bit and packet type pair
|
// Big-endian mask for version, padding bit and packet type pair
|
||||||
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe)
|
||||||
|
@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread
|
||||||
friend class RtspThread;
|
friend class RtspThread;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum
|
typedef enum
|
||||||
|
{
|
||||||
|
RTCP_SR = 200,
|
||||||
|
RTCP_RR = 201,
|
||||||
|
RTCP_SDES = 202,
|
||||||
|
RTCP_BYE = 203,
|
||||||
|
RTCP_APP = 204
|
||||||
|
} RtcpType;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
RTCP_SDES_END = 0,
|
||||||
|
RTCP_SDES_CNAME = 1,
|
||||||
|
RTCP_SDES_NAME = 2,
|
||||||
|
RTCP_SDES_EMAIL = 3,
|
||||||
|
RTCP_SDES_PHONE = 4,
|
||||||
|
RTCP_SDES_LOC = 5,
|
||||||
|
RTCP_SDES_TOOL = 6,
|
||||||
|
RTCP_SDES_NOTE = 7,
|
||||||
|
RTCP_SDES_PRIV = 8
|
||||||
|
} RtcpSdesType;
|
||||||
|
|
||||||
|
struct RtcpCommonHeader
|
||||||
|
{
|
||||||
|
uint8_t count:5; // varies by packet type
|
||||||
|
uint8_t p:1; // padding flag
|
||||||
|
uint8_t version:2; // protocol version
|
||||||
|
uint8_t pt; // RTCP packet type
|
||||||
|
uint16_t lenN; // pkt len in words, w/o this word, network order
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reception report block
|
||||||
|
struct RtcpRr
|
||||||
|
{
|
||||||
|
uint32_t ssrcN; // data source being reported
|
||||||
|
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||||
|
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||||
|
uint32_t lastSeqN; // extended last seq. no. received, network order
|
||||||
|
uint32_t jitterN; // interarrival jitter, network order
|
||||||
|
uint32_t lsrN; // last SR packet from this source, network order
|
||||||
|
uint32_t dlsrN; // delay since last SR packet, network order
|
||||||
|
};
|
||||||
|
|
||||||
|
// SDES item
|
||||||
|
struct RtcpSdesItem
|
||||||
|
{
|
||||||
|
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||||
|
uint8_t len; // length of item (in octets)
|
||||||
|
char data[]; // text, not null-terminated
|
||||||
|
};
|
||||||
|
|
||||||
|
// RTCP packet
|
||||||
|
struct RtcpPacket
|
||||||
|
{
|
||||||
|
RtcpCommonHeader header; // common header
|
||||||
|
union
|
||||||
{
|
{
|
||||||
RTCP_SR = 200,
|
// Sender Report (SR)
|
||||||
RTCP_RR = 201,
|
struct Sr
|
||||||
RTCP_SDES = 202,
|
{
|
||||||
RTCP_BYE = 203,
|
uint32_t ssrcN; // sender generating this report, network order
|
||||||
RTCP_APP = 204
|
uint32_t ntpSecN; // NTP timestamp, network order
|
||||||
} RtcpType;
|
uint32_t ntpFracN;
|
||||||
|
uint32_t rtpTsN; // RTP timestamp, network order
|
||||||
|
uint32_t pSentN; // packets sent, network order
|
||||||
|
uint32_t oSentN; // octets sent, network order
|
||||||
|
RtcpRr rr[]; // variable-length list
|
||||||
|
} sr;
|
||||||
|
|
||||||
typedef enum
|
// Reception Report (RR)
|
||||||
{
|
struct Rr
|
||||||
RTCP_SDES_END = 0,
|
{
|
||||||
RTCP_SDES_CNAME = 1,
|
uint32_t ssrcN; // receiver generating this report
|
||||||
RTCP_SDES_NAME = 2,
|
RtcpRr rr[]; // variable-length list
|
||||||
RTCP_SDES_EMAIL = 3,
|
} rr;
|
||||||
RTCP_SDES_PHONE = 4,
|
|
||||||
RTCP_SDES_LOC = 5,
|
|
||||||
RTCP_SDES_TOOL = 6,
|
|
||||||
RTCP_SDES_NOTE = 7,
|
|
||||||
RTCP_SDES_PRIV = 8
|
|
||||||
} RtcpSdesType;
|
|
||||||
|
|
||||||
struct RtcpCommonHeader
|
// source description (SDES)
|
||||||
{
|
struct Sdes
|
||||||
uint8_t count:5; // varies by packet type
|
{
|
||||||
uint8_t p:1; // padding flag
|
uint32_t srcN; // first SSRC/CSRC
|
||||||
uint8_t version:2; // protocol version
|
RtcpSdesItem item[]; // list of SDES items
|
||||||
uint8_t pt; // RTCP packet type
|
} sdes;
|
||||||
uint16_t lenN; // pkt len in words, w/o this word, network order
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reception report block
|
// BYE
|
||||||
struct RtcpRr
|
struct Bye
|
||||||
{
|
{
|
||||||
uint32_t ssrcN; // data source being reported
|
uint32_t srcN[]; // list of sources
|
||||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
} bye;
|
||||||
uint32_t lastSeqN; // extended last seq. no. received, network order
|
} body;
|
||||||
uint32_t jitterN; // interarrival jitter, network order
|
};
|
||||||
uint32_t lsrN; // last SR packet from this source, network order
|
|
||||||
uint32_t dlsrN; // delay since last SR packet, network order
|
|
||||||
};
|
|
||||||
|
|
||||||
// SDES item
|
|
||||||
struct RtcpSdesItem
|
|
||||||
{
|
|
||||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
|
||||||
uint8_t len; // length of item (in octets)
|
|
||||||
char data[]; // text, not null-terminated
|
|
||||||
};
|
|
||||||
|
|
||||||
// RTCP packet
|
|
||||||
struct RtcpPacket
|
|
||||||
{
|
|
||||||
RtcpCommonHeader header; // common header
|
|
||||||
union
|
|
||||||
{
|
|
||||||
// Sender Report (SR)
|
|
||||||
struct Sr
|
|
||||||
{
|
|
||||||
uint32_t ssrcN; // sender generating this report, network order
|
|
||||||
uint32_t ntpSecN; // NTP timestamp, network order
|
|
||||||
uint32_t ntpFracN;
|
|
||||||
uint32_t rtpTsN; // RTP timestamp, network order
|
|
||||||
uint32_t pSentN; // packets sent, network order
|
|
||||||
uint32_t oSentN; // octets sent, network order
|
|
||||||
RtcpRr rr[]; // variable-length list
|
|
||||||
} sr;
|
|
||||||
|
|
||||||
// Reception Report (RR)
|
|
||||||
struct Rr
|
|
||||||
{
|
|
||||||
uint32_t ssrcN; // receiver generating this report
|
|
||||||
RtcpRr rr[]; // variable-length list
|
|
||||||
} rr;
|
|
||||||
|
|
||||||
// source description (SDES)
|
|
||||||
struct Sdes
|
|
||||||
{
|
|
||||||
uint32_t srcN; // first SSRC/CSRC
|
|
||||||
RtcpSdesItem item[]; // list of SDES items
|
|
||||||
} sdes;
|
|
||||||
|
|
||||||
// BYE
|
|
||||||
struct Bye
|
|
||||||
{
|
|
||||||
uint32_t srcN[]; // list of sources
|
|
||||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
|
||||||
} bye;
|
|
||||||
} body;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtspThread &mRtspThread;
|
RtspThread &mRtspThread;
|
||||||
RtpSource &mRtpSource;
|
RtpSource &mRtpSource;
|
||||||
int mPort;
|
int mPort;
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
int recvPacket( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
int generateRr( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
int generateSdes( const unsigned char *packet, ssize_t packetLen );
|
||||||
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
int generateBye( const unsigned char *packet, ssize_t packetLen );
|
||||||
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
int recvPackets( unsigned char *buffer, ssize_t nBytes );
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTP_CTRL_H
|
#endif // ZM_RTP_CTRL_H
|
||||||
|
|
|
@ -33,88 +33,88 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m
|
||||||
|
|
||||||
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen )
|
||||||
{
|
{
|
||||||
const RtpDataHeader *rtpHeader;
|
const RtpDataHeader *rtpHeader;
|
||||||
rtpHeader = (RtpDataHeader *)packet;
|
rtpHeader = (RtpDataHeader *)packet;
|
||||||
|
|
||||||
//printf( "D: " );
|
//printf( "D: " );
|
||||||
//for ( int i = 0; i < 32; i++ )
|
//for ( int i = 0; i < 32; i++ )
|
||||||
//printf( "%02x ", (unsigned char)packet[i] );
|
//printf( "%02x ", (unsigned char)packet[i] );
|
||||||
//printf( "\n" );
|
//printf( "\n" );
|
||||||
|
|
||||||
Debug( 5, "Ver: %d", rtpHeader->version );
|
Debug( 5, "Ver: %d", rtpHeader->version );
|
||||||
Debug( 5, "P: %d", rtpHeader->p );
|
Debug( 5, "P: %d", rtpHeader->p );
|
||||||
Debug( 5, "Pt: %d", rtpHeader->pt );
|
Debug( 5, "Pt: %d", rtpHeader->pt );
|
||||||
Debug( 5, "Mk: %d", rtpHeader->m );
|
Debug( 5, "Mk: %d", rtpHeader->m );
|
||||||
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) );
|
||||||
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) );
|
||||||
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) );
|
||||||
|
|
||||||
//unsigned short seq = ntohs(rtpHeader->seqN);
|
//unsigned short seq = ntohs(rtpHeader->seqN);
|
||||||
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
unsigned long ssrc = ntohl(rtpHeader->ssrcN);
|
||||||
|
|
||||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||||
{
|
{
|
||||||
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
Warning( "Discarding packet for unrecognised ssrc %lx", ssrc );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
return( mRtpSource.handlePacket( packet, packetLen ) );
|
return( mRtpSource.handlePacket( packet, packetLen ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int RtpDataThread::run()
|
int RtpDataThread::run()
|
||||||
{
|
{
|
||||||
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() );
|
||||||
|
|
||||||
SockAddrInet localAddr;
|
SockAddrInet localAddr;
|
||||||
UdpInetServer rtpDataSocket;
|
UdpInetServer rtpDataSocket;
|
||||||
if ( mRtpSource.getLocalHost() != "" )
|
if ( mRtpSource.getLocalHost() != "" )
|
||||||
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" );
|
||||||
else
|
else
|
||||||
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" );
|
||||||
if ( !rtpDataSocket.bind( localAddr ) )
|
if ( !rtpDataSocket.bind( localAddr ) )
|
||||||
Fatal( "Failed to bind RTP server" );
|
Fatal( "Failed to bind RTP server" );
|
||||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() );
|
||||||
|
|
||||||
Select select( 3 );
|
Select select( 3 );
|
||||||
select.addReader( &rtpDataSocket );
|
select.addReader( &rtpDataSocket );
|
||||||
|
|
||||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||||
while ( !mStop && select.wait() >= 0 )
|
while ( !mStop && select.wait() >= 0 )
|
||||||
{
|
{
|
||||||
if ( mStop )
|
if ( mStop )
|
||||||
break;
|
break;
|
||||||
Select::CommsList readable = select.getReadable();
|
Select::CommsList readable = select.getReadable();
|
||||||
if ( readable.size() == 0 )
|
if ( readable.size() == 0 )
|
||||||
|
{
|
||||||
|
Error( "RTP timed out" );
|
||||||
|
mStop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
||||||
|
{
|
||||||
|
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
||||||
|
{
|
||||||
|
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||||
|
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||||
|
if ( nBytes )
|
||||||
{
|
{
|
||||||
Error( "RTP timed out" );
|
recvPacket( buffer, nBytes );
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ )
|
else
|
||||||
{
|
{
|
||||||
if ( UdpInetServer *socket = dynamic_cast<UdpInetServer *>(*iter) )
|
mStop = true;
|
||||||
{
|
break;
|
||||||
int nBytes = socket->recv( buffer, sizeof(buffer) );
|
|
||||||
Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() );
|
|
||||||
if ( nBytes )
|
|
||||||
{
|
|
||||||
recvPacket( buffer, nBytes );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mStop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Panic( "Barfed" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rtpDataSocket.close();
|
else
|
||||||
mRtspThread.stop();
|
{
|
||||||
return( 0 );
|
Panic( "Barfed" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rtpDataSocket.close();
|
||||||
|
mRtspThread.stop();
|
||||||
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -30,16 +30,16 @@ class RtpSource;
|
||||||
|
|
||||||
struct RtpDataHeader
|
struct RtpDataHeader
|
||||||
{
|
{
|
||||||
uint8_t cc:4; // CSRC count
|
uint8_t cc:4; // CSRC count
|
||||||
uint8_t x:1; // header extension flag
|
uint8_t x:1; // header extension flag
|
||||||
uint8_t p:1; // padding flag
|
uint8_t p:1; // padding flag
|
||||||
uint8_t version:2; // protocol version
|
uint8_t version:2; // protocol version
|
||||||
uint8_t pt:7; // payload type
|
uint8_t pt:7; // payload type
|
||||||
uint8_t m:1; // marker bit
|
uint8_t m:1; // marker bit
|
||||||
uint16_t seqN; // sequence number, network order
|
uint16_t seqN; // sequence number, network order
|
||||||
uint32_t timestampN; // timestamp, network order
|
uint32_t timestampN; // timestamp, network order
|
||||||
uint32_t ssrcN; // synchronization source, network order
|
uint32_t ssrcN; // synchronization source, network order
|
||||||
uint32_t csrc[]; // optional CSRC list
|
uint32_t csrc[]; // optional CSRC list
|
||||||
};
|
};
|
||||||
|
|
||||||
class RtpDataThread : public Thread
|
class RtpDataThread : public Thread
|
||||||
|
@ -47,21 +47,21 @@ class RtpDataThread : public Thread
|
||||||
friend class RtspThread;
|
friend class RtspThread;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RtspThread &mRtspThread;
|
RtspThread &mRtspThread;
|
||||||
RtpSource &mRtpSource;
|
RtpSource &mRtpSource;
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
bool recvPacket( const unsigned char *packet, size_t packetLen );
|
||||||
int run();
|
int run();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTP_DATA_H
|
#endif // ZM_RTP_DATA_H
|
||||||
|
|
|
@ -27,358 +27,358 @@
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
|
|
||||||
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
|
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
|
||||||
mId( id ),
|
mId( id ),
|
||||||
mSsrc( ssrc ),
|
mSsrc( ssrc ),
|
||||||
mLocalHost( localHost ),
|
mLocalHost( localHost ),
|
||||||
mRemoteHost( remoteHost ),
|
mRemoteHost( remoteHost ),
|
||||||
mRtpClock( rtpClock ),
|
mRtpClock( rtpClock ),
|
||||||
mCodecId( codecId ),
|
mCodecId( codecId ),
|
||||||
mFrame( 65536 ),
|
mFrame( 65536 ),
|
||||||
mFrameCount( 0 ),
|
mFrameCount( 0 ),
|
||||||
mFrameGood( true ),
|
mFrameGood( true ),
|
||||||
mFrameReady( false ),
|
mFrameReady( false ),
|
||||||
mFrameProcessed( false )
|
mFrameProcessed( false )
|
||||||
{
|
{
|
||||||
char hostname[256] = "";
|
char hostname[256] = "";
|
||||||
gethostname( hostname, sizeof(hostname) );
|
gethostname( hostname, sizeof(hostname) );
|
||||||
|
|
||||||
mCname = stringtf( "zm-%d@%s", mId, hostname );
|
mCname = stringtf( "zm-%d@%s", mId, hostname );
|
||||||
Debug( 3, "RTP CName = %s", mCname.c_str() );
|
Debug( 3, "RTP CName = %s", mCname.c_str() );
|
||||||
|
|
||||||
init( seq );
|
init( seq );
|
||||||
mMaxSeq = seq - 1;
|
mMaxSeq = seq - 1;
|
||||||
mProbation = MIN_SEQUENTIAL;
|
mProbation = MIN_SEQUENTIAL;
|
||||||
|
|
||||||
mLocalPortChans[0] = localPortBase;
|
mLocalPortChans[0] = localPortBase;
|
||||||
mLocalPortChans[1] = localPortBase+1;
|
mLocalPortChans[1] = localPortBase+1;
|
||||||
|
|
||||||
mRemotePortChans[0] = remotePortBase;
|
mRemotePortChans[0] = remotePortBase;
|
||||||
mRemotePortChans[1] = remotePortBase+1;
|
mRemotePortChans[1] = remotePortBase+1;
|
||||||
|
|
||||||
mRtpFactor = mRtpClock;
|
mRtpFactor = mRtpClock;
|
||||||
|
|
||||||
mBaseTimeReal = tvNow();
|
mBaseTimeReal = tvNow();
|
||||||
mBaseTimeNtp = tvZero();
|
mBaseTimeNtp = tvZero();
|
||||||
mBaseTimeRtp = rtpTime;
|
mBaseTimeRtp = rtpTime;
|
||||||
|
|
||||||
mLastSrTimeReal = tvZero();
|
mLastSrTimeReal = tvZero();
|
||||||
mLastSrTimeNtp = tvZero();
|
mLastSrTimeNtp = tvZero();
|
||||||
mLastSrTimeRtp = 0;
|
mLastSrTimeRtp = 0;
|
||||||
|
|
||||||
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
|
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4)
|
||||||
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSource::init( uint16_t seq )
|
void RtpSource::init( uint16_t seq )
|
||||||
{
|
{
|
||||||
Debug( 3, "Initialising sequence" );
|
Debug( 3, "Initialising sequence" );
|
||||||
mBaseSeq = seq;
|
mBaseSeq = seq;
|
||||||
mMaxSeq = seq;
|
mMaxSeq = seq;
|
||||||
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
|
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
|
||||||
mCycles = 0;
|
mCycles = 0;
|
||||||
mReceivedPackets = 0;
|
mReceivedPackets = 0;
|
||||||
mReceivedPrior = 0;
|
mReceivedPrior = 0;
|
||||||
mExpectedPrior = 0;
|
mExpectedPrior = 0;
|
||||||
// other initialization
|
// other initialization
|
||||||
mJitter = 0;
|
mJitter = 0;
|
||||||
mTransit = 0;
|
mTransit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::updateSeq( uint16_t seq )
|
bool RtpSource::updateSeq( uint16_t seq )
|
||||||
{
|
{
|
||||||
uint16_t uDelta = seq - mMaxSeq;
|
uint16_t uDelta = seq - mMaxSeq;
|
||||||
|
|
||||||
// Source is not valid until MIN_SEQUENTIAL packets with
|
// Source is not valid until MIN_SEQUENTIAL packets with
|
||||||
// sequential sequence numbers have been received.
|
// sequential sequence numbers have been received.
|
||||||
Debug( 5, "Seq: %d", seq );
|
Debug( 5, "Seq: %d", seq );
|
||||||
|
|
||||||
if ( mProbation)
|
if ( mProbation)
|
||||||
|
{
|
||||||
|
// packet is in sequence
|
||||||
|
if ( seq == mMaxSeq + 1)
|
||||||
{
|
{
|
||||||
// packet is in sequence
|
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
||||||
if ( seq == mMaxSeq + 1)
|
mProbation--;
|
||||||
{
|
mMaxSeq = seq;
|
||||||
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
if ( mProbation == 0 )
|
||||||
mProbation--;
|
{
|
||||||
mMaxSeq = seq;
|
init( seq );
|
||||||
if ( mProbation == 0 )
|
mReceivedPackets++;
|
||||||
{
|
|
||||||
init( seq );
|
|
||||||
mReceivedPackets++;
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
|
||||||
mProbation = MIN_SEQUENTIAL - 1;
|
|
||||||
mMaxSeq = seq;
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
else if ( uDelta < MAX_DROPOUT )
|
|
||||||
{
|
|
||||||
if ( uDelta == 1 )
|
|
||||||
{
|
|
||||||
Debug( 3, "Packet in sequence, gap %d", uDelta );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Warning( "Packet in sequence, gap %d", uDelta );
|
|
||||||
}
|
|
||||||
|
|
||||||
// in order, with permissible gap
|
|
||||||
if ( seq < mMaxSeq )
|
|
||||||
{
|
|
||||||
// Sequence number wrapped - count another 64K cycle.
|
|
||||||
mCycles += RTP_SEQ_MOD;
|
|
||||||
}
|
|
||||||
mMaxSeq = seq;
|
|
||||||
}
|
|
||||||
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
|
|
||||||
{
|
|
||||||
Warning( "Packet out of sequence, gap %d", uDelta );
|
|
||||||
// the sequence number made a very large jump
|
|
||||||
if ( seq == mBadSeq )
|
|
||||||
{
|
|
||||||
Debug( 3, "Restarting sequence" );
|
|
||||||
// Two sequential packets -- assume that the other side
|
|
||||||
// restarted without telling us so just re-sync
|
|
||||||
// (i.e., pretend this was the first packet).
|
|
||||||
init( seq );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
||||||
// duplicate or reordered packet
|
mProbation = MIN_SEQUENTIAL - 1;
|
||||||
return( false );
|
mMaxSeq = seq;
|
||||||
|
return( false );
|
||||||
}
|
}
|
||||||
mReceivedPackets++;
|
return( true );
|
||||||
return( uDelta==1?true:false );
|
}
|
||||||
|
else if ( uDelta < MAX_DROPOUT )
|
||||||
|
{
|
||||||
|
if ( uDelta == 1 )
|
||||||
|
{
|
||||||
|
Debug( 3, "Packet in sequence, gap %d", uDelta );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning( "Packet in sequence, gap %d", uDelta );
|
||||||
|
}
|
||||||
|
|
||||||
|
// in order, with permissible gap
|
||||||
|
if ( seq < mMaxSeq )
|
||||||
|
{
|
||||||
|
// Sequence number wrapped - count another 64K cycle.
|
||||||
|
mCycles += RTP_SEQ_MOD;
|
||||||
|
}
|
||||||
|
mMaxSeq = seq;
|
||||||
|
}
|
||||||
|
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
|
||||||
|
{
|
||||||
|
Warning( "Packet out of sequence, gap %d", uDelta );
|
||||||
|
// the sequence number made a very large jump
|
||||||
|
if ( seq == mBadSeq )
|
||||||
|
{
|
||||||
|
Debug( 3, "Restarting sequence" );
|
||||||
|
// Two sequential packets -- assume that the other side
|
||||||
|
// restarted without telling us so just re-sync
|
||||||
|
// (i.e., pretend this was the first packet).
|
||||||
|
init( seq );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
||||||
|
// duplicate or reordered packet
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
mReceivedPackets++;
|
||||||
|
return( uDelta==1?true:false );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSource::updateJitter( const RtpDataHeader *header )
|
void RtpSource::updateJitter( const RtpDataHeader *header )
|
||||||
{
|
{
|
||||||
if ( mRtpFactor > 0 )
|
if ( mRtpFactor > 0 )
|
||||||
{
|
{
|
||||||
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
||||||
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
||||||
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
||||||
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
|
Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) );
|
||||||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||||
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
||||||
|
|
||||||
if ( mTransit > 0 )
|
if ( mTransit > 0 )
|
||||||
{
|
|
||||||
// Jitter
|
|
||||||
int d = packetTransit - mTransit;
|
|
||||||
Debug( 5, "Jitter D = %d", d );
|
|
||||||
if ( d < 0 )
|
|
||||||
d = -d;
|
|
||||||
//mJitter += (1./16.) * ((double)d - mJitter);
|
|
||||||
mJitter += d - ((mJitter + 8) >> 4);
|
|
||||||
}
|
|
||||||
mTransit = packetTransit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
mJitter = 0;
|
// Jitter
|
||||||
|
int d = packetTransit - mTransit;
|
||||||
|
Debug( 5, "Jitter D = %d", d );
|
||||||
|
if ( d < 0 )
|
||||||
|
d = -d;
|
||||||
|
//mJitter += (1./16.) * ((double)d - mJitter);
|
||||||
|
mJitter += d - ((mJitter + 8) >> 4);
|
||||||
}
|
}
|
||||||
Debug( 5, "RTP Jitter: %d", mJitter );
|
mTransit = packetTransit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mJitter = 0;
|
||||||
|
}
|
||||||
|
Debug( 5, "RTP Jitter: %d", mJitter );
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
|
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
|
||||||
{
|
{
|
||||||
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
||||||
|
|
||||||
|
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||||
|
|
||||||
|
if ( mBaseTimeNtp.tv_sec == 0 )
|
||||||
|
{
|
||||||
|
mBaseTimeReal = tvNow();
|
||||||
|
mBaseTimeNtp = ntpTime;
|
||||||
|
mBaseTimeRtp = rtpTime;
|
||||||
|
}
|
||||||
|
else if ( !mRtpClock )
|
||||||
|
{
|
||||||
|
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
|
||||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||||
|
|
||||||
if ( mBaseTimeNtp.tv_sec == 0 )
|
double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime );
|
||||||
{
|
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
||||||
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 );
|
//Debug( 5, "Real-diff: %.6f", diffRealTime );
|
||||||
uint32_t diffRtpTime = rtpTime - mBaseTimeRtp;
|
Debug( 5, "NTP-diff: %.6f", diffNtpTime );
|
||||||
|
Debug( 5, "RTP-diff: %d", diffRtpTime );
|
||||||
|
|
||||||
//Debug( 5, "Real-diff: %.6f", diffRealTime );
|
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
||||||
Debug( 5, "NTP-diff: %.6f", diffNtpTime );
|
|
||||||
Debug( 5, "RTP-diff: %d", diffRtpTime );
|
|
||||||
|
|
||||||
mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime);
|
Debug( 5, "RTPfactor: %d", mRtpFactor );
|
||||||
|
}
|
||||||
Debug( 5, "RTPfactor: %d", mRtpFactor );
|
mLastSrTimeNtpSecs = ntpTimeSecs;
|
||||||
}
|
mLastSrTimeNtpFrac = ntpTimeFrac;
|
||||||
mLastSrTimeNtpSecs = ntpTimeSecs;
|
mLastSrTimeNtp = ntpTime;
|
||||||
mLastSrTimeNtpFrac = ntpTimeFrac;
|
mLastSrTimeRtp = rtpTime;
|
||||||
mLastSrTimeNtp = ntpTime;
|
|
||||||
mLastSrTimeRtp = rtpTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RtpSource::updateRtcpStats()
|
void RtpSource::updateRtcpStats()
|
||||||
{
|
{
|
||||||
uint32_t extendedMax = mCycles + mMaxSeq;
|
uint32_t extendedMax = mCycles + mMaxSeq;
|
||||||
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
||||||
|
|
||||||
Debug( 5, "Expected packets = %d", mExpectedPackets );
|
Debug( 5, "Expected packets = %d", mExpectedPackets );
|
||||||
|
|
||||||
// The number of packets lost is defined to be the number of packets
|
// The number of packets lost is defined to be the number of packets
|
||||||
// expected less the number of packets actually received:
|
// expected less the number of packets actually received:
|
||||||
mLostPackets = mExpectedPackets - mReceivedPackets;
|
mLostPackets = mExpectedPackets - mReceivedPackets;
|
||||||
Debug( 5, "Lost packets = %d", mLostPackets );
|
Debug( 5, "Lost packets = %d", mLostPackets );
|
||||||
|
|
||||||
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
uint32_t expectedInterval = mExpectedPackets - mExpectedPrior;
|
||||||
Debug( 5, "Expected interval = %d", expectedInterval );
|
Debug( 5, "Expected interval = %d", expectedInterval );
|
||||||
mExpectedPrior = mExpectedPackets;
|
mExpectedPrior = mExpectedPackets;
|
||||||
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
uint32_t receivedInterval = mReceivedPackets - mReceivedPrior;
|
||||||
Debug( 5, "Received interval = %d", receivedInterval );
|
Debug( 5, "Received interval = %d", receivedInterval );
|
||||||
mReceivedPrior = mReceivedPackets;
|
mReceivedPrior = mReceivedPackets;
|
||||||
uint32_t lostInterval = expectedInterval - receivedInterval;
|
uint32_t lostInterval = expectedInterval - receivedInterval;
|
||||||
Debug( 5, "Lost interval = %d", lostInterval );
|
Debug( 5, "Lost interval = %d", lostInterval );
|
||||||
|
|
||||||
if ( expectedInterval == 0 || lostInterval <= 0 )
|
if ( expectedInterval == 0 || lostInterval <= 0 )
|
||||||
mLostFraction = 0;
|
mLostFraction = 0;
|
||||||
else
|
else
|
||||||
mLostFraction = (lostInterval << 8) / expectedInterval;
|
mLostFraction = (lostInterval << 8) / expectedInterval;
|
||||||
Debug( 5, "Lost fraction = %d", mLostFraction );
|
Debug( 5, "Lost fraction = %d", mLostFraction );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||||
{
|
{
|
||||||
const RtpDataHeader *rtpHeader;
|
const RtpDataHeader *rtpHeader;
|
||||||
rtpHeader = (RtpDataHeader *)packet;
|
rtpHeader = (RtpDataHeader *)packet;
|
||||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||||
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
||||||
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
||||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||||
|
|
||||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||||
|
{
|
||||||
|
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
||||||
|
|
||||||
|
if ( mFrameGood )
|
||||||
{
|
{
|
||||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
int extraHeader = 0;
|
||||||
|
|
||||||
if ( mFrameGood )
|
if( mCodecId == AV_CODEC_ID_H264 )
|
||||||
|
{
|
||||||
|
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||||
|
|
||||||
|
switch (nalType)
|
||||||
{
|
{
|
||||||
int extraHeader = 0;
|
case 24:
|
||||||
|
{
|
||||||
if( mCodecId == AV_CODEC_ID_H264 )
|
extraHeader = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 25: case 26: case 27:
|
||||||
|
{
|
||||||
|
extraHeader = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FU-A and FU-B
|
||||||
|
case 28: case 29:
|
||||||
|
{
|
||||||
|
// Is this NAL the first NAL in fragmentation sequence
|
||||||
|
if ( packet[rtpHeaderSize+1] & 0x80 )
|
||||||
{
|
{
|
||||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
// Now we will form new header of frame
|
||||||
|
mFrame.append( "\x0\x0\x1\x0", 4 );
|
||||||
switch (nalType)
|
// Reconstruct NAL header from FU headers
|
||||||
{
|
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
|
||||||
case 24:
|
(packet[rtpHeaderSize] & 0xe0);
|
||||||
{
|
|
||||||
extraHeader = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 25: case 26: case 27:
|
|
||||||
{
|
|
||||||
extraHeader = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// FU-A and FU-B
|
|
||||||
case 28: case 29:
|
|
||||||
{
|
|
||||||
// Is this NAL the first NAL in fragmentation sequence
|
|
||||||
if ( packet[rtpHeaderSize+1] & 0x80 )
|
|
||||||
{
|
|
||||||
// Now we will form new header of frame
|
|
||||||
mFrame.append( "\x0\x0\x1\x0", 4 );
|
|
||||||
// Reconstruct NAL header from FU headers
|
|
||||||
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
|
|
||||||
(packet[rtpHeaderSize] & 0xe0);
|
|
||||||
}
|
|
||||||
|
|
||||||
extraHeader = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append NAL frame start code
|
|
||||||
if ( !mFrame.size() )
|
|
||||||
mFrame.append( "\x0\x0\x1", 3 );
|
|
||||||
}
|
}
|
||||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
|
||||||
|
extraHeader = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Hexdump( 4, mFrame.head(), 16 );
|
// Append NAL frame start code
|
||||||
|
if ( !mFrame.size() )
|
||||||
if ( thisM )
|
mFrame.append( "\x0\x0\x1", 3 );
|
||||||
{
|
}
|
||||||
if ( mFrameGood )
|
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hexdump( 4, mFrame.head(), 16 );
|
||||||
|
|
||||||
if ( thisM )
|
if ( thisM )
|
||||||
{
|
{
|
||||||
mFrameGood = true;
|
if ( mFrameGood )
|
||||||
prevM = true;
|
{
|
||||||
|
Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||||
|
|
||||||
|
mFrameProcessed.setValueImmediate( false );
|
||||||
|
mFrameReady.updateValueSignal( true );
|
||||||
|
if ( !mFrameProcessed.getValueImmediate() )
|
||||||
|
{
|
||||||
|
for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ )
|
||||||
|
if( count > 1 )
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
mFrameCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||||
|
}
|
||||||
|
mFrame.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( mFrame.size() )
|
||||||
|
{
|
||||||
|
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
prevM = false;
|
{
|
||||||
|
Warning( "Discarding frame %d", mFrameCount );
|
||||||
|
}
|
||||||
|
mFrameGood = false;
|
||||||
|
mFrame.clear();
|
||||||
|
}
|
||||||
|
if ( thisM )
|
||||||
|
{
|
||||||
|
mFrameGood = true;
|
||||||
|
prevM = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prevM = false;
|
||||||
|
|
||||||
updateJitter( rtpHeader );
|
updateJitter( rtpHeader );
|
||||||
|
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RtpSource::getFrame( Buffer &buffer )
|
bool RtpSource::getFrame( Buffer &buffer )
|
||||||
{
|
{
|
||||||
Debug( 3, "Getting frame" );
|
Debug( 3, "Getting frame" );
|
||||||
if ( !mFrameReady.getValueImmediate() )
|
if ( !mFrameReady.getValueImmediate() )
|
||||||
{
|
{
|
||||||
// Allow for a couple of spurious returns
|
// Allow for a couple of spurious returns
|
||||||
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
|
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
|
||||||
if ( count > 1 )
|
if ( count > 1 )
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
buffer = mFrame;
|
buffer = mFrame;
|
||||||
mFrameReady.setValueImmediate( false );
|
mFrameReady.setValueImmediate( false );
|
||||||
mFrameProcessed.updateValueSignal( true );
|
mFrameProcessed.updateValueSignal( true );
|
||||||
Debug( 3, "Copied %d bytes", buffer.size() );
|
Debug( 3, "Copied %d bytes", buffer.size() );
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
|
@ -35,152 +35,152 @@ struct RtpDataHeader;
|
||||||
class RtpSource
|
class RtpSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { EMPTY, FILLING, READY } FrameState;
|
typedef enum { EMPTY, FILLING, READY } FrameState;
|
||||||
private:
|
private:
|
||||||
static const int RTP_SEQ_MOD = 1<<16;
|
static const int RTP_SEQ_MOD = 1<<16;
|
||||||
static const int MAX_DROPOUT = 3000;
|
static const int MAX_DROPOUT = 3000;
|
||||||
static const int MAX_MISORDER = 100;
|
static const int MAX_MISORDER = 100;
|
||||||
static const int MIN_SEQUENTIAL = 2;
|
static const int MIN_SEQUENTIAL = 2;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Identity
|
// Identity
|
||||||
int mId; // General id (usually monitor id)
|
int mId; // General id (usually monitor id)
|
||||||
std::string mCname; // Canonical name, for SDES
|
std::string mCname; // Canonical name, for SDES
|
||||||
|
|
||||||
// RTP/RTCP fields
|
// RTP/RTCP fields
|
||||||
uint32_t mSsrc;
|
uint32_t mSsrc;
|
||||||
uint16_t mMaxSeq; // highest seq. number seen
|
uint16_t mMaxSeq; // highest seq. number seen
|
||||||
uint32_t mCycles; // shifted count of seq. number cycles
|
uint32_t mCycles; // shifted count of seq. number cycles
|
||||||
uint32_t mBaseSeq; // base seq number
|
uint32_t mBaseSeq; // base seq number
|
||||||
uint32_t mBadSeq; // last 'bad' seq number + 1
|
uint32_t mBadSeq; // last 'bad' seq number + 1
|
||||||
uint32_t mProbation; // sequ. packets till source is valid
|
uint32_t mProbation; // sequ. packets till source is valid
|
||||||
uint32_t mReceivedPackets; // packets received
|
uint32_t mReceivedPackets; // packets received
|
||||||
uint32_t mExpectedPrior; // packet expected at last interval
|
uint32_t mExpectedPrior; // packet expected at last interval
|
||||||
uint32_t mReceivedPrior; // packet received at last interval
|
uint32_t mReceivedPrior; // packet received at last interval
|
||||||
uint32_t mTransit; // relative trans time for prev pkt
|
uint32_t mTransit; // relative trans time for prev pkt
|
||||||
uint32_t mJitter; // estimated jitter
|
uint32_t mJitter; // estimated jitter
|
||||||
|
|
||||||
// Ports/Channels
|
// Ports/Channels
|
||||||
std::string mLocalHost;
|
std::string mLocalHost;
|
||||||
int mLocalPortChans[2];
|
int mLocalPortChans[2];
|
||||||
std::string mRemoteHost;
|
std::string mRemoteHost;
|
||||||
int mRemotePortChans[2];
|
int mRemotePortChans[2];
|
||||||
|
|
||||||
// Time keys
|
// Time keys
|
||||||
uint32_t mRtpClock;
|
uint32_t mRtpClock;
|
||||||
uint32_t mRtpFactor;
|
uint32_t mRtpFactor;
|
||||||
struct timeval mBaseTimeReal;
|
struct timeval mBaseTimeReal;
|
||||||
struct timeval mBaseTimeNtp;
|
struct timeval mBaseTimeNtp;
|
||||||
uint32_t mBaseTimeRtp;
|
uint32_t mBaseTimeRtp;
|
||||||
|
|
||||||
struct timeval mLastSrTimeReal;
|
struct timeval mLastSrTimeReal;
|
||||||
uint32_t mLastSrTimeNtpSecs;
|
uint32_t mLastSrTimeNtpSecs;
|
||||||
uint32_t mLastSrTimeNtpFrac;
|
uint32_t mLastSrTimeNtpFrac;
|
||||||
struct timeval mLastSrTimeNtp;
|
struct timeval mLastSrTimeNtp;
|
||||||
uint32_t mLastSrTimeRtp;
|
uint32_t mLastSrTimeRtp;
|
||||||
|
|
||||||
// Stats, intermittently updated
|
// Stats, intermittently updated
|
||||||
uint32_t mExpectedPackets;
|
uint32_t mExpectedPackets;
|
||||||
uint32_t mLostPackets;
|
uint32_t mLostPackets;
|
||||||
uint8_t mLostFraction;
|
uint8_t mLostFraction;
|
||||||
|
|
||||||
_AVCODECID mCodecId;
|
_AVCODECID mCodecId;
|
||||||
|
|
||||||
Buffer mFrame;
|
Buffer mFrame;
|
||||||
int mFrameCount;
|
int mFrameCount;
|
||||||
bool mFrameGood;
|
bool mFrameGood;
|
||||||
bool prevM;
|
bool prevM;
|
||||||
ThreadData<bool> mFrameReady;
|
ThreadData<bool> mFrameReady;
|
||||||
ThreadData<bool> mFrameProcessed;
|
ThreadData<bool> mFrameProcessed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init( uint16_t seq );
|
void init( uint16_t seq );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId );
|
||||||
|
|
||||||
bool updateSeq( uint16_t seq );
|
bool updateSeq( uint16_t seq );
|
||||||
void updateJitter( const RtpDataHeader *header );
|
void updateJitter( const RtpDataHeader *header );
|
||||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||||
void updateRtcpStats();
|
void updateRtcpStats();
|
||||||
|
|
||||||
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
bool handlePacket( const unsigned char *packet, size_t packetLen );
|
||||||
|
|
||||||
uint32_t getSsrc() const
|
uint32_t getSsrc() const
|
||||||
{
|
{
|
||||||
return( mSsrc );
|
return( mSsrc );
|
||||||
}
|
}
|
||||||
void setSsrc( uint32_t ssrc )
|
void setSsrc( uint32_t ssrc )
|
||||||
{
|
{
|
||||||
mSsrc = ssrc;
|
mSsrc = ssrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getFrame( Buffer &buffer );
|
bool getFrame( Buffer &buffer );
|
||||||
|
|
||||||
const std::string &getCname() const
|
const std::string &getCname() const
|
||||||
{
|
{
|
||||||
return( mCname );
|
return( mCname );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &getLocalHost() const
|
const std::string &getLocalHost() const
|
||||||
{
|
{
|
||||||
return( mLocalHost );
|
return( mLocalHost );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLocalDataPort() const
|
int getLocalDataPort() const
|
||||||
{
|
{
|
||||||
return( mLocalPortChans[0] );
|
return( mLocalPortChans[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getLocalCtrlPort() const
|
int getLocalCtrlPort() const
|
||||||
{
|
{
|
||||||
return( mLocalPortChans[1] );
|
return( mLocalPortChans[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &getRemoteHost() const
|
const std::string &getRemoteHost() const
|
||||||
{
|
{
|
||||||
return( mRemoteHost );
|
return( mRemoteHost );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRemoteDataPort() const
|
int getRemoteDataPort() const
|
||||||
{
|
{
|
||||||
return( mRemotePortChans[0] );
|
return( mRemotePortChans[0] );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRemoteCtrlPort() const
|
int getRemoteCtrlPort() const
|
||||||
{
|
{
|
||||||
return( mRemotePortChans[1] );
|
return( mRemotePortChans[1] );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getMaxSeq() const
|
uint32_t getMaxSeq() const
|
||||||
{
|
{
|
||||||
return( mCycles + mMaxSeq );
|
return( mCycles + mMaxSeq );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getExpectedPackets() const
|
uint32_t getExpectedPackets() const
|
||||||
{
|
{
|
||||||
return( mExpectedPackets );
|
return( mExpectedPackets );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLostPackets() const
|
uint32_t getLostPackets() const
|
||||||
{
|
{
|
||||||
return( mLostPackets );
|
return( mLostPackets );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getLostFraction() const
|
uint8_t getLostFraction() const
|
||||||
{
|
{
|
||||||
return( mLostFraction );
|
return( mLostFraction );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getJitter() const
|
uint32_t getJitter() const
|
||||||
{
|
{
|
||||||
return( mJitter >> 4 );
|
return( mJitter >> 4 );
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getLastSrTimestamp() const
|
uint32_t getLastSrTimestamp() const
|
||||||
{
|
{
|
||||||
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
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
|
class RtspThread : public Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod;
|
||||||
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::set<int> PortSet;
|
typedef std::set<int> PortSet;
|
||||||
typedef std::set<uint32_t> SsrcSet;
|
typedef std::set<uint32_t> SsrcSet;
|
||||||
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
typedef std::map<uint32_t,RtpSource *> SourceMap;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int smMinDataPort;
|
static int smMinDataPort;
|
||||||
static int smMaxDataPort;
|
static int smMaxDataPort;
|
||||||
static PortSet smLocalSsrcs;
|
static PortSet smLocalSsrcs;
|
||||||
static PortSet smAssignedPorts;
|
static PortSet smAssignedPorts;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mId;
|
int mId;
|
||||||
|
|
||||||
RtspMethod mMethod;
|
RtspMethod mMethod;
|
||||||
std::string mProtocol;
|
std::string mProtocol;
|
||||||
std::string mHost;
|
std::string mHost;
|
||||||
std::string mPort;
|
std::string mPort;
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
bool mRtspDescribe;
|
bool mRtspDescribe;
|
||||||
std::string mUrl;
|
std::string mUrl;
|
||||||
|
|
||||||
// Reworked authentication system
|
// Reworked authentication system
|
||||||
// First try without authentication, even if we have a username and password
|
// First try without authentication, even if we have a username and password
|
||||||
// on receiving a 401 response, select authentication method (basic or digest)
|
// on receiving a 401 response, select authentication method (basic or digest)
|
||||||
// fill required fields and set needAuth
|
// fill required fields and set needAuth
|
||||||
// subsequent requests can set the required authentication header.
|
// subsequent requests can set the required authentication header.
|
||||||
bool mNeedAuth;
|
bool mNeedAuth;
|
||||||
int respCode;
|
int respCode;
|
||||||
zm::Authenticator* mAuthenticator;
|
zm::Authenticator* mAuthenticator;
|
||||||
|
|
||||||
|
|
||||||
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
std::string mHttpSession; ///< Only for RTSP over HTTP sessions
|
||||||
|
|
||||||
TcpInetClient mRtspSocket;
|
TcpInetClient mRtspSocket;
|
||||||
TcpInetClient mRtspSocket2;
|
TcpInetClient mRtspSocket2;
|
||||||
|
|
||||||
SourceMap mSources;
|
SourceMap mSources;
|
||||||
|
|
||||||
SessionDescriptor *mSessDesc;
|
SessionDescriptor *mSessDesc;
|
||||||
AVFormatContext *mFormatContext;
|
AVFormatContext *mFormatContext;
|
||||||
|
|
||||||
uint16_t mSeq;
|
uint16_t mSeq;
|
||||||
uint32_t mSession;
|
uint32_t mSession;
|
||||||
uint32_t mSsrc;
|
uint32_t mSsrc;
|
||||||
|
|
||||||
int mRemotePorts[2];
|
int mRemotePorts[2];
|
||||||
int mRemoteChannels[2];
|
int mRemoteChannels[2];
|
||||||
RtspDist mDist;
|
RtspDist mDist;
|
||||||
|
|
||||||
unsigned long mRtpTime;
|
unsigned long mRtpTime;
|
||||||
|
|
||||||
bool mStop;
|
bool mStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool sendCommand( std::string message );
|
bool sendCommand( std::string message );
|
||||||
bool recvResponse( std::string &response );
|
bool recvResponse( std::string &response );
|
||||||
void checkAuthResponse(std::string &response);
|
void checkAuthResponse(std::string &response);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe );
|
||||||
~RtspThread();
|
~RtspThread();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int requestPorts();
|
int requestPorts();
|
||||||
void releasePorts( int port );
|
void releasePorts( int port );
|
||||||
|
|
||||||
bool isValidSsrc( uint32_t ssrc );
|
bool isValidSsrc( uint32_t ssrc );
|
||||||
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header );
|
||||||
|
|
||||||
uint32_t getSsrc() const
|
uint32_t getSsrc() const
|
||||||
{
|
{
|
||||||
return( mSsrc );
|
return( mSsrc );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSources() const
|
bool hasSources() const
|
||||||
{
|
{
|
||||||
return( !mSources.empty() );
|
return( !mSources.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *getFormatContext()
|
AVFormatContext *getFormatContext()
|
||||||
{
|
{
|
||||||
return( mFormatContext );
|
return( mFormatContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getFrame( Buffer &frame )
|
bool getFrame( Buffer &frame )
|
||||||
{
|
{
|
||||||
SourceMap::iterator iter = mSources.begin();
|
SourceMap::iterator iter = mSources.begin();
|
||||||
if ( iter == mSources.end() )
|
if ( iter == mSources.end() )
|
||||||
return( false );
|
return( false );
|
||||||
return( iter->second->getFrame( frame ) );
|
return( iter->second->getFrame( frame ) );
|
||||||
}
|
}
|
||||||
int run();
|
int run();
|
||||||
void stop()
|
void stop()
|
||||||
{
|
{
|
||||||
mStop = true;
|
mStop = true;
|
||||||
}
|
}
|
||||||
bool stopped() const
|
bool stopped() const
|
||||||
{
|
{
|
||||||
return( mStop );
|
return( mStop );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_RTSP_H
|
#endif // ZM_RTSP_H
|
||||||
|
|
|
@ -28,206 +28,206 @@ namespace zm {
|
||||||
|
|
||||||
Authenticator::Authenticator(std::string &username, std::string password) {
|
Authenticator::Authenticator(std::string &username, std::string password) {
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal( "Unable to initialise libgcrypt" );
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
fAuthMethod = AUTH_UNDEFINED;
|
fAuthMethod = AUTH_UNDEFINED;
|
||||||
fUsername = username;
|
fUsername = username;
|
||||||
fPassword = password;
|
fPassword = password;
|
||||||
nc = 1;
|
nc = 1;
|
||||||
fCnonce = "0a4f113b";
|
fCnonce = "0a4f113b";
|
||||||
}
|
}
|
||||||
|
|
||||||
Authenticator::~Authenticator() {
|
Authenticator::~Authenticator() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::reset() {
|
void Authenticator::reset() {
|
||||||
fRealm.clear();
|
fRealm.clear();
|
||||||
fNonce.clear();
|
fNonce.clear();
|
||||||
fUsername.clear();
|
fUsername.clear();
|
||||||
fPassword.clear();
|
fPassword.clear();
|
||||||
fAuthMethod = AUTH_UNDEFINED;
|
fAuthMethod = AUTH_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::authHandleHeader(std::string headerData)
|
void Authenticator::authHandleHeader(std::string headerData)
|
||||||
{
|
{
|
||||||
const char* basic_match = "Basic ";
|
const char* basic_match = "Basic ";
|
||||||
const char* digest_match = "Digest ";
|
const char* digest_match = "Digest ";
|
||||||
size_t digest_match_len = strlen(digest_match);
|
size_t digest_match_len = strlen(digest_match);
|
||||||
|
|
||||||
// Check if basic auth
|
// Check if basic auth
|
||||||
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0)
|
||||||
|
{
|
||||||
|
fAuthMethod = AUTH_BASIC;
|
||||||
|
Debug( 2, "Set authMethod to Basic");
|
||||||
|
}
|
||||||
|
// Check if digest auth
|
||||||
|
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
||||||
|
{
|
||||||
|
fAuthMethod = AUTH_DIGEST;
|
||||||
|
Debug( 2, "Set authMethod to Digest");
|
||||||
|
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
||||||
|
// subparts are key="value"
|
||||||
|
for ( size_t i = 0; i < subparts.size(); i++ )
|
||||||
{
|
{
|
||||||
fAuthMethod = AUTH_BASIC;
|
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
||||||
Debug( 2, "Set authMethod to Basic");
|
std::string key = trimSpaces( kvPair[0] );
|
||||||
}
|
if (key == "realm") {
|
||||||
// Check if digest auth
|
fRealm = trimSet( kvPair[1], "\"");
|
||||||
else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0)
|
continue;
|
||||||
{
|
}
|
||||||
fAuthMethod = AUTH_DIGEST;
|
if (key == "nonce") {
|
||||||
Debug( 2, "Set authMethod to Digest");
|
fNonce = trimSet( kvPair[1], "\"");
|
||||||
StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ",");
|
continue;
|
||||||
// subparts are key="value"
|
}
|
||||||
for ( size_t i = 0; i < subparts.size(); i++ )
|
if (key == "qop") {
|
||||||
{
|
fQop = trimSet( kvPair[1], "\"");
|
||||||
StringVector kvPair = split( trimSpaces( subparts[i] ), "=" );
|
continue;
|
||||||
std::string key = trimSpaces( kvPair[0] );
|
}
|
||||||
if (key == "realm") {
|
|
||||||
fRealm = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == "nonce") {
|
|
||||||
fNonce = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == "qop") {
|
|
||||||
fQop = trimSet( kvPair[1], "\"");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
|
||||||
}
|
}
|
||||||
|
Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::quote(std::string src)
|
std::string Authenticator::quote(std::string src)
|
||||||
{
|
{
|
||||||
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
std::string Authenticator::getAuthHeader(std::string method, std::string uri)
|
||||||
{
|
{
|
||||||
std::string result = "Authorization: ";
|
std::string result = "Authorization: ";
|
||||||
if (fAuthMethod == AUTH_BASIC)
|
if (fAuthMethod == AUTH_BASIC)
|
||||||
{
|
{
|
||||||
result += "Basic " + base64Encode( username() + ":" + password() );
|
result += "Basic " + base64Encode( username() + ":" + password() );
|
||||||
|
}
|
||||||
|
else if (fAuthMethod == AUTH_DIGEST)
|
||||||
|
{
|
||||||
|
result += std::string("Digest ") +
|
||||||
|
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
||||||
|
"nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\"";
|
||||||
|
if ( ! fQop.empty() ) {
|
||||||
|
result += ", qop=" + fQop;
|
||||||
|
result += ", nc=" + stringtf("%08x",nc);
|
||||||
|
result += ", cnonce=\"" + fCnonce + "\"";
|
||||||
}
|
}
|
||||||
else if (fAuthMethod == AUTH_DIGEST)
|
result += ", response=\"" + computeDigestResponse(method, uri) + "\"";
|
||||||
{
|
result += ", algorithm=\"MD5\"";
|
||||||
result += std::string("Digest ") +
|
|
||||||
"username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " +
|
|
||||||
"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\"";
|
|
||||||
|
|
||||||
//Authorization: Digest username="zm",
|
//Authorization: Digest username="zm",
|
||||||
// realm="NC-336PW-HD-1080P",
|
// realm="NC-336PW-HD-1080P",
|
||||||
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
// nonce="de8859d97609a6fcc16eaba490dcfd80",
|
||||||
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
// uri="rtsp://10.192.16.8:554/live/0/h264.sdp",
|
||||||
// response="4092120557d3099a163bd51a0d59744d",
|
// response="4092120557d3099a163bd51a0d59744d",
|
||||||
// algorithm=MD5,
|
// algorithm=MD5,
|
||||||
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
// opaque="5ccc069c403ebaf9f0171e9517f40e41",
|
||||||
// qop="auth",
|
// qop="auth",
|
||||||
// cnonce="c8051140765877dc",
|
// cnonce="c8051140765877dc",
|
||||||
// nc=00000001
|
// nc=00000001
|
||||||
|
|
||||||
}
|
}
|
||||||
result += "\r\n";
|
result += "\r\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) {
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
// The "response" field is computed as:
|
// The "response" field is computed as:
|
||||||
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
// md5(md5(<username>:<realm>:<password>):<nonce>:md5(<cmd>:<url>))
|
||||||
size_t md5len = 16;
|
size_t md5len = 16;
|
||||||
unsigned char md5buf[md5len];
|
unsigned char md5buf[md5len];
|
||||||
char md5HexBuf[md5len*2+1];
|
char md5HexBuf[md5len*2+1];
|
||||||
|
|
||||||
// Step 1: md5(<username>:<realm>:<password>)
|
// Step 1: md5(<username>:<realm>:<password>)
|
||||||
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
std::string ha1Data = username() + ":" + realm() + ":" + password();
|
||||||
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
std::string ha1Hash = md5HexBuf;
|
std::string ha1Hash = md5HexBuf;
|
||||||
|
|
||||||
// Step 2: md5(<cmd>:<url>)
|
// Step 2: md5(<cmd>:<url>)
|
||||||
std::string ha2Data = method + ":" + uri;
|
std::string ha2Data = method + ":" + uri;
|
||||||
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf );
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
std::string ha2Hash = md5HexBuf;
|
std::string ha2Hash = md5HexBuf;
|
||||||
|
|
||||||
// Step 3: md5(ha1:<nonce>:ha2)
|
// Step 3: md5(ha1:<nonce>:ha2)
|
||||||
std::string digestData = ha1Hash + ":" + nonce();
|
std::string digestData = ha1Hash + ":" + nonce();
|
||||||
if ( ! fQop.empty() ) {
|
if ( ! fQop.empty() ) {
|
||||||
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop;
|
||||||
nc ++;
|
nc ++;
|
||||||
// if qop was specified, then we have to include t and a cnonce and an nccount
|
// if qop was specified, then we have to include t and a cnonce and an nccount
|
||||||
}
|
}
|
||||||
digestData += ":" + ha2Hash;
|
digestData += ":" + ha2Hash;
|
||||||
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
Debug( 2, "pre-md5: %s", digestData.c_str() );
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf);
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len );
|
||||||
#endif
|
#endif
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] );
|
||||||
}
|
}
|
||||||
md5HexBuf[md5len*2]='\0';
|
md5HexBuf[md5len*2]='\0';
|
||||||
|
|
||||||
return md5HexBuf;
|
return md5HexBuf;
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5
|
||||||
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
Error( "You need to build with gnutls or openssl installed to use digest authentication" );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5
|
||||||
}
|
}
|
||||||
|
|
||||||
void Authenticator::checkAuthResponse(std::string &response) {
|
void Authenticator::checkAuthResponse(std::string &response) {
|
||||||
std::string authLine;
|
std::string authLine;
|
||||||
StringVector lines = split( response, "\r\n" );
|
StringVector lines = split( response, "\r\n" );
|
||||||
const char* authenticate_match = "WWW-Authenticate:";
|
const char* authenticate_match = "WWW-Authenticate:";
|
||||||
size_t authenticate_match_len = strlen(authenticate_match);
|
size_t authenticate_match_len = strlen(authenticate_match);
|
||||||
|
|
||||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||||
// stop at end of headers
|
// stop at end of headers
|
||||||
if (lines[i].length()==0)
|
if (lines[i].length()==0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) {
|
||||||
authLine = lines[i];
|
authLine = lines[i];
|
||||||
Debug( 2, "Found auth line at %d", i);
|
Debug( 2, "Found auth line at %d", i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!authLine.empty()) {
|
if (!authLine.empty()) {
|
||||||
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
Debug( 2, "Analyze auth line %s", authLine.c_str());
|
||||||
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) );
|
||||||
} else {
|
} else {
|
||||||
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
Debug( 2, "Didn't find auth line in %s", authLine.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zm
|
} // namespace zm
|
||||||
|
|
|
@ -37,19 +37,19 @@ namespace zm {
|
||||||
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 };
|
||||||
class Authenticator {
|
class Authenticator {
|
||||||
public:
|
public:
|
||||||
Authenticator(std::string &username, std::string password);
|
Authenticator(std::string &username, std::string password);
|
||||||
virtual ~Authenticator();
|
virtual ~Authenticator();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
std::string realm() { return fRealm; }
|
std::string realm() { return fRealm; }
|
||||||
std::string nonce() { return fNonce; }
|
std::string nonce() { return fNonce; }
|
||||||
std::string username() { return fUsername; }
|
std::string username() { return fUsername; }
|
||||||
AuthMethod auth_method() const { return fAuthMethod; }
|
AuthMethod auth_method() const { return fAuthMethod; }
|
||||||
|
|
||||||
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
std::string computeDigestResponse( std::string &cmd, std::string &url );
|
||||||
void authHandleHeader( std::string headerData );
|
void authHandleHeader( std::string headerData );
|
||||||
std::string getAuthHeader( std::string method, std::string path );
|
std::string getAuthHeader( std::string method, std::string path );
|
||||||
void checkAuthResponse(std::string &response);
|
void checkAuthResponse(std::string &response);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string password() { return fPassword; }
|
std::string password() { return fPassword; }
|
||||||
|
@ -61,7 +61,7 @@ private:
|
||||||
std::string fUsername;
|
std::string fUsername;
|
||||||
std::string fPassword;
|
std::string fPassword;
|
||||||
std::string quote( std::string src );
|
std::string quote( std::string src );
|
||||||
int nc;
|
int nc;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zm
|
} // namespace zm
|
||||||
|
|
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))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||||
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
||||||
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
||||||
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||||
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||||
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||||
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
||||||
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
||||||
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
||||||
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
||||||
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
||||||
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
||||||
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
||||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||||
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||||
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
||||||
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
||||||
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||||
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
||||||
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
||||||
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
||||||
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
||||||
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
||||||
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
||||||
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
||||||
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
||||||
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
||||||
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
||||||
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
||||||
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
||||||
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
||||||
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
||||||
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
||||||
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
||||||
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
||||||
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
||||||
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
||||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
||||||
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
||||||
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
||||||
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
||||||
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
||||||
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
||||||
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
||||||
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
||||||
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
||||||
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
||||||
mTtl( 16 ),
|
mTtl( 16 ),
|
||||||
mNoAddresses( 0 )
|
mNoAddresses( 0 )
|
||||||
{
|
{
|
||||||
StringVector tokens = split( connInfo, " " );
|
StringVector tokens = split( connInfo, " " );
|
||||||
if ( tokens.size() < 3 )
|
if ( tokens.size() < 3 )
|
||||||
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
||||||
mNetworkType = tokens[0];
|
mNetworkType = tokens[0];
|
||||||
if ( mNetworkType != "IN" )
|
if ( mNetworkType != "IN" )
|
||||||
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||||
mAddressType = tokens[1];
|
mAddressType = tokens[1];
|
||||||
if ( mAddressType != "IP4" )
|
if ( mAddressType != "IP4" )
|
||||||
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
||||||
StringVector addressTokens = split( tokens[2], "/" );
|
StringVector addressTokens = split( tokens[2], "/" );
|
||||||
if ( addressTokens.size() < 1 )
|
if ( addressTokens.size() < 1 )
|
||||||
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
||||||
mAddress = addressTokens[0];
|
mAddress = addressTokens[0];
|
||||||
if ( addressTokens.size() >= 2 )
|
if ( addressTokens.size() >= 2 )
|
||||||
mTtl = atoi(addressTokens[1].c_str());
|
mTtl = atoi(addressTokens[1].c_str());
|
||||||
if ( addressTokens.size() >= 3 )
|
if ( addressTokens.size() >= 3 )
|
||||||
mNoAddresses = atoi(addressTokens[2].c_str());
|
mNoAddresses = atoi(addressTokens[2].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
||||||
mValue( 0 )
|
mValue( 0 )
|
||||||
{
|
{
|
||||||
StringVector tokens = split( bandInfo, ":" );
|
StringVector tokens = split( bandInfo, ":" );
|
||||||
if ( tokens.size() < 2 )
|
if ( tokens.size() < 2 )
|
||||||
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
||||||
mType = tokens[0];
|
mType = tokens[0];
|
||||||
//if ( mNetworkType != "IN" )
|
//if ( mNetworkType != "IN" )
|
||||||
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
||||||
mValue = atoi(tokens[1].c_str());
|
mValue = atoi(tokens[1].c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||||
mType( type ),
|
mType( type ),
|
||||||
mPort( port ),
|
mPort( port ),
|
||||||
mNumPorts( numPorts ),
|
mNumPorts( numPorts ),
|
||||||
mTransport( transport ),
|
mTransport( transport ),
|
||||||
mPayloadType( payloadType ),
|
mPayloadType( payloadType ),
|
||||||
mFrameRate( 0.0 ),
|
mFrameRate( 0.0 ),
|
||||||
mClock( 0 ),
|
mClock( 0 ),
|
||||||
mWidth( 0 ),
|
mWidth( 0 ),
|
||||||
mHeight( 0 ),
|
mHeight( 0 ),
|
||||||
mSprops( "" ),
|
mSprops( "" ),
|
||||||
mConnInfo( 0 )
|
mConnInfo( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
||||||
mUrl( url ),
|
mUrl( url ),
|
||||||
mConnInfo( 0 ),
|
mConnInfo( 0 ),
|
||||||
mBandInfo( 0 )
|
mBandInfo( 0 )
|
||||||
{
|
{
|
||||||
MediaDescriptor *currMedia = 0;
|
MediaDescriptor *currMedia = 0;
|
||||||
|
|
||||||
StringVector lines = split( sdp, "\r\n" );
|
StringVector lines = split( sdp, "\r\n" );
|
||||||
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ )
|
||||||
|
{
|
||||||
|
std::string line = *iter;
|
||||||
|
if ( line.empty() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
||||||
|
const char sdpType = line[0];
|
||||||
|
if ( line[1] != '=' )
|
||||||
|
throw Exception( "Invalid SDP format at '"+line+"'" );
|
||||||
|
|
||||||
|
line.erase( 0, 2 );
|
||||||
|
switch( sdpType )
|
||||||
{
|
{
|
||||||
std::string line = *iter;
|
case 'v' :
|
||||||
if ( line.empty() )
|
mVersion = line;
|
||||||
break;
|
break;
|
||||||
|
case 'o' :
|
||||||
Debug( 3, "Processing SDP line '%s'", line.c_str() );
|
mOwner = line;
|
||||||
const char sdpType = line[0];
|
break;
|
||||||
if ( line[1] != '=' )
|
case 's' :
|
||||||
throw Exception( "Invalid SDP format at '"+line+"'" );
|
mName = line;
|
||||||
|
break;
|
||||||
line.erase( 0, 2 );
|
case 'i' :
|
||||||
switch( sdpType )
|
mInfo = line;
|
||||||
|
break;
|
||||||
|
case 'c' :
|
||||||
|
// This prevent a memory leak if the field appears more than one time
|
||||||
|
if ( mConnInfo )
|
||||||
|
delete mConnInfo;
|
||||||
|
mConnInfo = new ConnInfo( line );
|
||||||
|
break;
|
||||||
|
case 'b' :
|
||||||
|
// This prevent a memory leak if the field appears more than one time
|
||||||
|
if ( mBandInfo )
|
||||||
|
delete mBandInfo;
|
||||||
|
mBandInfo = new BandInfo( line );
|
||||||
|
break;
|
||||||
|
case 't' :
|
||||||
|
mTimeInfo = line;
|
||||||
|
break;
|
||||||
|
case 'a' :
|
||||||
|
{
|
||||||
|
mAttributes.push_back( line );
|
||||||
|
StringVector tokens = split( line, ":", 2 );
|
||||||
|
std::string attrName = tokens[0];
|
||||||
|
if ( currMedia )
|
||||||
{
|
{
|
||||||
case 'v' :
|
if ( attrName == "control" )
|
||||||
mVersion = line;
|
{
|
||||||
break;
|
if ( tokens.size() < 2 )
|
||||||
case 'o' :
|
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
mOwner = line;
|
currMedia->setControlUrl( tokens[1] );
|
||||||
break;
|
}
|
||||||
case 's' :
|
else if ( attrName == "range" )
|
||||||
mName = line;
|
{
|
||||||
break;
|
}
|
||||||
case 'i' :
|
else if ( attrName == "rtpmap" )
|
||||||
mInfo = line;
|
{
|
||||||
break;
|
// a=rtpmap:96 MP4V-ES/90000
|
||||||
case 'c' :
|
if ( tokens.size() < 2 )
|
||||||
// This prevent a memory leak if the field appears more than one time
|
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||||
if ( mConnInfo )
|
StringVector attrTokens = split( tokens[1], " " );
|
||||||
delete mConnInfo;
|
int payloadType = atoi(attrTokens[0].c_str());
|
||||||
mConnInfo = new ConnInfo( line );
|
if ( payloadType != currMedia->getPayloadType() )
|
||||||
break;
|
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||||
case 'b' :
|
std::string payloadDesc = attrTokens[1];
|
||||||
// This prevent a memory leak if the field appears more than one time
|
//currMedia->setPayloadType( payloadType );
|
||||||
if ( mBandInfo )
|
if ( attrTokens.size() > 1 )
|
||||||
delete mBandInfo;
|
|
||||||
mBandInfo = new BandInfo( line );
|
|
||||||
break;
|
|
||||||
case 't' :
|
|
||||||
mTimeInfo = line;
|
|
||||||
break;
|
|
||||||
case 'a' :
|
|
||||||
{
|
{
|
||||||
mAttributes.push_back( line );
|
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||||
StringVector tokens = split( line, ":", 2 );
|
std::string payloadDesc = payloadTokens[0];
|
||||||
std::string attrName = tokens[0];
|
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||||
if ( currMedia )
|
currMedia->setPayloadDesc( payloadDesc );
|
||||||
|
currMedia->setClock( payloadClock );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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" )
|
}
|
||||||
{
|
else if ( attr3Tokens[0] == "config" )
|
||||||
if ( tokens.size() < 2 )
|
{
|
||||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
}
|
||||||
currMedia->setControlUrl( tokens[1] );
|
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||||
}
|
{
|
||||||
else if ( attrName == "range" )
|
size_t t = attr2Tokens[i].find("=");
|
||||||
{
|
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||||
}
|
Debug(4, "sprop-parameter-sets value %s", c);
|
||||||
else if ( attrName == "rtpmap" )
|
currMedia->setSprops(std::string(c));
|
||||||
{
|
}
|
||||||
// a=rtpmap:96 MP4V-ES/90000
|
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
|
||||||
if ( tokens.size() < 2 )
|
{
|
||||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
size_t t = attr2Tokens[i].find("=");
|
||||||
StringVector attrTokens = split( tokens[1], " " );
|
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
||||||
int payloadType = atoi(attrTokens[0].c_str());
|
Debug(4, "sprop-parameter-sets value %s", c);
|
||||||
if ( payloadType != currMedia->getPayloadType() )
|
currMedia->setSprops(std::string(c));
|
||||||
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
|
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()
|
SessionDescriptor::~SessionDescriptor()
|
||||||
{
|
{
|
||||||
if ( mConnInfo )
|
if ( mConnInfo )
|
||||||
delete mConnInfo;
|
delete mConnInfo;
|
||||||
if ( mBandInfo )
|
if ( mBandInfo )
|
||||||
delete mBandInfo;
|
delete mBandInfo;
|
||||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||||
delete mMediaList[i];
|
delete mMediaList[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
||||||
{
|
{
|
||||||
AVFormatContext *formatContext = avformat_alloc_context();
|
AVFormatContext *formatContext = avformat_alloc_context();
|
||||||
|
|
||||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||||
/*
|
/*
|
||||||
if ( mName.length() )
|
if ( mName.length() )
|
||||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||||
if ( mInfo.length() )
|
if ( mInfo.length() )
|
||||||
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
||||||
*/
|
*/
|
||||||
//formatContext->nb_streams = mMediaList.size();
|
//formatContext->nb_streams = mMediaList.size();
|
||||||
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
||||||
{
|
{
|
||||||
const MediaDescriptor *mediaDesc = mMediaList[i];
|
const MediaDescriptor *mediaDesc = mMediaList[i];
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
||||||
AVStream *stream = av_new_stream( formatContext, i );
|
AVStream *stream = av_new_stream( formatContext, i );
|
||||||
#else
|
#else
|
||||||
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
AVStream *stream = avformat_new_stream( formatContext, NULL );
|
||||||
stream->id = i;
|
stream->id = i;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
if ( mediaDesc->getType() == "video" )
|
if ( mediaDesc->getType() == "video" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
else if ( mediaDesc->getType() == "audio" )
|
else if ( mediaDesc->getType() == "audio" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
stream->codec->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
else if ( mediaDesc->getType() == "application" )
|
else if ( mediaDesc->getType() == "application" )
|
||||||
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
stream->codec->codec_type = AVMEDIA_TYPE_DATA;
|
||||||
#else
|
#else
|
||||||
if ( mediaDesc->getType() == "video" )
|
if ( mediaDesc->getType() == "video" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
stream->codec->codec_type = CODEC_TYPE_VIDEO;
|
||||||
else if ( mediaDesc->getType() == "audio" )
|
else if ( mediaDesc->getType() == "audio" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
stream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||||
else if ( mediaDesc->getType() == "application" )
|
else if ( mediaDesc->getType() == "application" )
|
||||||
stream->codec->codec_type = CODEC_TYPE_DATA;
|
stream->codec->codec_type = CODEC_TYPE_DATA;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
std::string codec_name;
|
std::string codec_name;
|
||||||
#endif
|
#endif
|
||||||
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC )
|
||||||
|
{
|
||||||
|
// Look in static table
|
||||||
|
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
||||||
|
{
|
||||||
|
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
||||||
{
|
{
|
||||||
// Look in static table
|
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
||||||
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ )
|
|
||||||
{
|
|
||||||
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() )
|
|
||||||
{
|
|
||||||
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||||
#else
|
#else
|
||||||
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||||
#endif
|
#endif
|
||||||
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
stream->codec->codec_type = smStaticPayloads[i].codecType;
|
||||||
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
stream->codec->codec_id = smStaticPayloads[i].codecId;
|
||||||
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
stream->codec->sample_rate = smStaticPayloads[i].clockRate;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Look in dynamic table
|
||||||
|
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
||||||
|
{
|
||||||
|
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
||||||
{
|
{
|
||||||
// Look in dynamic table
|
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
||||||
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ )
|
|
||||||
{
|
|
||||||
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() )
|
|
||||||
{
|
|
||||||
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName );
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
codec_name = std::string( smStaticPayloads[i].payloadName );
|
codec_name = std::string( smStaticPayloads[i].payloadName );
|
||||||
#else
|
#else
|
||||||
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );;
|
||||||
#endif
|
#endif
|
||||||
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
stream->codec->codec_type = smDynamicPayloads[i].codecType;
|
||||||
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
stream->codec->codec_id = smDynamicPayloads[i].codecId;
|
||||||
stream->codec->sample_rate = mediaDesc->getClock();
|
stream->codec->sample_rate = mediaDesc->getClock();
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
|
||||||
if ( codec_name.empty() )
|
|
||||||
#else
|
|
||||||
if ( !stream->codec->codec_name[0] )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
|
||||||
//return( 0 );
|
|
||||||
}
|
|
||||||
if ( mediaDesc->getWidth() )
|
|
||||||
stream->codec->width = mediaDesc->getWidth();
|
|
||||||
if ( mediaDesc->getHeight() )
|
|
||||||
stream->codec->height = mediaDesc->getHeight();
|
|
||||||
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
|
||||||
{
|
|
||||||
uint8_t start_sequence[]= { 0, 0, 1 };
|
|
||||||
stream->codec->extradata_size= 0;
|
|
||||||
stream->codec->extradata= NULL;
|
|
||||||
char pvalue[1024], *value = pvalue;
|
|
||||||
|
|
||||||
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
|
||||||
|
|
||||||
while (*value) {
|
|
||||||
char base64packet[1024];
|
|
||||||
uint8_t decoded_packet[1024];
|
|
||||||
uint32_t packet_size;
|
|
||||||
char *dst = base64packet;
|
|
||||||
|
|
||||||
while (*value && *value != ','
|
|
||||||
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
|
||||||
*dst++ = *value++;
|
|
||||||
}
|
|
||||||
*dst++ = '\0';
|
|
||||||
|
|
||||||
if (*value == ',')
|
|
||||||
value++;
|
|
||||||
|
|
||||||
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
|
||||||
Hexdump(4, (char *)decoded_packet, packet_size);
|
|
||||||
if (packet_size) {
|
|
||||||
uint8_t *dest =
|
|
||||||
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
|
||||||
stream->codec->extradata_size +
|
|
||||||
FF_INPUT_BUFFER_PADDING_SIZE);
|
|
||||||
if(dest) {
|
|
||||||
if(stream->codec->extradata_size) {
|
|
||||||
// av_realloc?
|
|
||||||
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
|
||||||
av_free(stream->codec->extradata);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
|
||||||
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
|
||||||
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
|
||||||
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
|
||||||
|
|
||||||
stream->codec->extradata= dest;
|
|
||||||
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
|
||||||
// } else {
|
|
||||||
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
|
||||||
// return AVERROR(ENOMEM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return( formatContext );
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||||
|
if ( codec_name.empty() )
|
||||||
|
#else
|
||||||
|
if ( !stream->codec->codec_name[0] )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||||
|
//return( 0 );
|
||||||
|
}
|
||||||
|
if ( mediaDesc->getWidth() )
|
||||||
|
stream->codec->width = mediaDesc->getWidth();
|
||||||
|
if ( mediaDesc->getHeight() )
|
||||||
|
stream->codec->height = mediaDesc->getHeight();
|
||||||
|
if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size())
|
||||||
|
{
|
||||||
|
uint8_t start_sequence[]= { 0, 0, 1 };
|
||||||
|
stream->codec->extradata_size= 0;
|
||||||
|
stream->codec->extradata= NULL;
|
||||||
|
char pvalue[1024], *value = pvalue;
|
||||||
|
|
||||||
|
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
||||||
|
|
||||||
|
while (*value) {
|
||||||
|
char base64packet[1024];
|
||||||
|
uint8_t decoded_packet[1024];
|
||||||
|
uint32_t packet_size;
|
||||||
|
char *dst = base64packet;
|
||||||
|
|
||||||
|
while (*value && *value != ','
|
||||||
|
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
||||||
|
*dst++ = *value++;
|
||||||
|
}
|
||||||
|
*dst++ = '\0';
|
||||||
|
|
||||||
|
if (*value == ',')
|
||||||
|
value++;
|
||||||
|
|
||||||
|
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
||||||
|
Hexdump(4, (char *)decoded_packet, packet_size);
|
||||||
|
if (packet_size) {
|
||||||
|
uint8_t *dest =
|
||||||
|
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
||||||
|
stream->codec->extradata_size +
|
||||||
|
FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if(dest) {
|
||||||
|
if(stream->codec->extradata_size) {
|
||||||
|
// av_realloc?
|
||||||
|
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
|
||||||
|
av_free(stream->codec->extradata);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
|
||||||
|
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
||||||
|
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
|
||||||
|
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
|
||||||
|
stream->codec->extradata= dest;
|
||||||
|
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
|
||||||
|
// } else {
|
||||||
|
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
||||||
|
// return AVERROR(ENOMEM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return( formatContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
348
src/zm_sdp.h
348
src/zm_sdp.h
|
@ -34,204 +34,204 @@
|
||||||
class SessionDescriptor
|
class SessionDescriptor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
enum { PAYLOAD_TYPE_DYNAMIC=96 };
|
||||||
|
|
||||||
struct StaticPayloadDesc
|
struct StaticPayloadDesc
|
||||||
{
|
{
|
||||||
int payloadType;
|
int payloadType;
|
||||||
const char payloadName[6];
|
const char payloadName[6];
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
AVMediaType codecType;
|
AVMediaType codecType;
|
||||||
#else
|
#else
|
||||||
enum CodecType codecType;
|
enum CodecType codecType;
|
||||||
#endif
|
#endif
|
||||||
_AVCODECID codecId;
|
_AVCODECID codecId;
|
||||||
int clockRate;
|
int clockRate;
|
||||||
int autoChannels;
|
int autoChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DynamicPayloadDesc
|
struct DynamicPayloadDesc
|
||||||
{
|
{
|
||||||
const char payloadName[32];
|
const char payloadName[32];
|
||||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
AVMediaType codecType;
|
AVMediaType codecType;
|
||||||
#else
|
#else
|
||||||
enum CodecType codecType;
|
enum CodecType codecType;
|
||||||
#endif
|
#endif
|
||||||
_AVCODECID codecId;
|
_AVCODECID codecId;
|
||||||
|
|
||||||
//int clockRate;
|
//int clockRate;
|
||||||
//int autoChannels;
|
//int autoChannels;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class ConnInfo
|
class ConnInfo
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mNetworkType;
|
std::string mNetworkType;
|
||||||
std::string mAddressType;
|
std::string mAddressType;
|
||||||
std::string mAddress;
|
std::string mAddress;
|
||||||
int mTtl;
|
int mTtl;
|
||||||
int mNoAddresses;
|
int mNoAddresses;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConnInfo( const std::string &connInfo );
|
ConnInfo( const std::string &connInfo );
|
||||||
};
|
};
|
||||||
|
|
||||||
class BandInfo
|
class BandInfo
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mType;
|
std::string mType;
|
||||||
int mValue;
|
int mValue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BandInfo( const std::string &bandInfo );
|
BandInfo( const std::string &bandInfo );
|
||||||
};
|
};
|
||||||
|
|
||||||
class MediaDescriptor
|
class MediaDescriptor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::string mType;
|
std::string mType;
|
||||||
int mPort;
|
int mPort;
|
||||||
int mNumPorts;
|
int mNumPorts;
|
||||||
std::string mTransport;
|
std::string mTransport;
|
||||||
int mPayloadType;
|
int mPayloadType;
|
||||||
|
|
||||||
std::string mPayloadDesc;
|
std::string mPayloadDesc;
|
||||||
std::string mControlUrl;
|
std::string mControlUrl;
|
||||||
double mFrameRate;
|
double mFrameRate;
|
||||||
int mClock;
|
int mClock;
|
||||||
int mWidth;
|
int mWidth;
|
||||||
int mHeight;
|
int mHeight;
|
||||||
std::string mSprops;
|
std::string mSprops;
|
||||||
|
|
||||||
ConnInfo *mConnInfo;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
|
||||||
|
|
||||||
const std::string &getType() const
|
|
||||||
{
|
|
||||||
return( mType );
|
|
||||||
}
|
|
||||||
int getPort() const
|
|
||||||
{
|
|
||||||
return( mPort );
|
|
||||||
}
|
|
||||||
int getNumPorts() const
|
|
||||||
{
|
|
||||||
return( mNumPorts );
|
|
||||||
}
|
|
||||||
const std::string &getTransport() const
|
|
||||||
{
|
|
||||||
return( mTransport );
|
|
||||||
}
|
|
||||||
const int getPayloadType() const
|
|
||||||
{
|
|
||||||
return( mPayloadType );
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getPayloadDesc() const
|
|
||||||
{
|
|
||||||
return( mPayloadDesc );
|
|
||||||
}
|
|
||||||
void setPayloadDesc( const std::string &payloadDesc )
|
|
||||||
{
|
|
||||||
mPayloadDesc = payloadDesc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getControlUrl() const
|
|
||||||
{
|
|
||||||
return( mControlUrl );
|
|
||||||
}
|
|
||||||
void setControlUrl( const std::string &controlUrl )
|
|
||||||
{
|
|
||||||
mControlUrl = controlUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int getClock() const
|
|
||||||
{
|
|
||||||
return( mClock );
|
|
||||||
}
|
|
||||||
void setClock( int clock )
|
|
||||||
{
|
|
||||||
mClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFrameSize( int width, int height )
|
|
||||||
{
|
|
||||||
mWidth = width;
|
|
||||||
mHeight = height;
|
|
||||||
}
|
|
||||||
int getWidth() const
|
|
||||||
{
|
|
||||||
return( mWidth );
|
|
||||||
}
|
|
||||||
int getHeight() const
|
|
||||||
{
|
|
||||||
return( mHeight );
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSprops(const std::string props)
|
|
||||||
{
|
|
||||||
mSprops = props;
|
|
||||||
}
|
|
||||||
const std::string getSprops() const
|
|
||||||
{
|
|
||||||
return ( mSprops );
|
|
||||||
}
|
|
||||||
const double getFrameRate() const
|
|
||||||
{
|
|
||||||
return( mFrameRate );
|
|
||||||
}
|
|
||||||
void setFrameRate( double frameRate )
|
|
||||||
{
|
|
||||||
mFrameRate = frameRate;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::vector<MediaDescriptor *> MediaList;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static StaticPayloadDesc smStaticPayloads[];
|
|
||||||
static DynamicPayloadDesc smDynamicPayloads[];
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string mUrl;
|
|
||||||
|
|
||||||
std::string mVersion;
|
|
||||||
std::string mOwner;
|
|
||||||
std::string mName;
|
|
||||||
std::string mInfo;
|
|
||||||
|
|
||||||
ConnInfo *mConnInfo;
|
ConnInfo *mConnInfo;
|
||||||
BandInfo *mBandInfo;
|
|
||||||
std::string mTimeInfo;
|
|
||||||
StringVector mAttributes;
|
|
||||||
|
|
||||||
MediaList mMediaList;
|
public:
|
||||||
|
MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType );
|
||||||
|
|
||||||
|
const std::string &getType() const
|
||||||
|
{
|
||||||
|
return( mType );
|
||||||
|
}
|
||||||
|
int getPort() const
|
||||||
|
{
|
||||||
|
return( mPort );
|
||||||
|
}
|
||||||
|
int getNumPorts() const
|
||||||
|
{
|
||||||
|
return( mNumPorts );
|
||||||
|
}
|
||||||
|
const std::string &getTransport() const
|
||||||
|
{
|
||||||
|
return( mTransport );
|
||||||
|
}
|
||||||
|
const int getPayloadType() const
|
||||||
|
{
|
||||||
|
return( mPayloadType );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getPayloadDesc() const
|
||||||
|
{
|
||||||
|
return( mPayloadDesc );
|
||||||
|
}
|
||||||
|
void setPayloadDesc( const std::string &payloadDesc )
|
||||||
|
{
|
||||||
|
mPayloadDesc = payloadDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &getControlUrl() const
|
||||||
|
{
|
||||||
|
return( mControlUrl );
|
||||||
|
}
|
||||||
|
void setControlUrl( const std::string &controlUrl )
|
||||||
|
{
|
||||||
|
mControlUrl = controlUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int getClock() const
|
||||||
|
{
|
||||||
|
return( mClock );
|
||||||
|
}
|
||||||
|
void setClock( int clock )
|
||||||
|
{
|
||||||
|
mClock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFrameSize( int width, int height )
|
||||||
|
{
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
}
|
||||||
|
int getWidth() const
|
||||||
|
{
|
||||||
|
return( mWidth );
|
||||||
|
}
|
||||||
|
int getHeight() const
|
||||||
|
{
|
||||||
|
return( mHeight );
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSprops(const std::string props)
|
||||||
|
{
|
||||||
|
mSprops = props;
|
||||||
|
}
|
||||||
|
const std::string getSprops() const
|
||||||
|
{
|
||||||
|
return ( mSprops );
|
||||||
|
}
|
||||||
|
const double getFrameRate() const
|
||||||
|
{
|
||||||
|
return( mFrameRate );
|
||||||
|
}
|
||||||
|
void setFrameRate( double frameRate )
|
||||||
|
{
|
||||||
|
mFrameRate = frameRate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<MediaDescriptor *> MediaList;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static StaticPayloadDesc smStaticPayloads[];
|
||||||
|
static DynamicPayloadDesc smDynamicPayloads[];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string mUrl;
|
||||||
|
|
||||||
|
std::string mVersion;
|
||||||
|
std::string mOwner;
|
||||||
|
std::string mName;
|
||||||
|
std::string mInfo;
|
||||||
|
|
||||||
|
ConnInfo *mConnInfo;
|
||||||
|
BandInfo *mBandInfo;
|
||||||
|
std::string mTimeInfo;
|
||||||
|
StringVector mAttributes;
|
||||||
|
|
||||||
|
MediaList mMediaList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SessionDescriptor( const std::string &url, const std::string &sdp );
|
SessionDescriptor( const std::string &url, const std::string &sdp );
|
||||||
~SessionDescriptor();
|
~SessionDescriptor();
|
||||||
|
|
||||||
const std::string &getUrl() const
|
const std::string &getUrl() const
|
||||||
{
|
{
|
||||||
return( mUrl );
|
return( mUrl );
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNumStreams() const
|
int getNumStreams() const
|
||||||
{
|
{
|
||||||
return( mMediaList.size() );
|
return( mMediaList.size() );
|
||||||
}
|
}
|
||||||
MediaDescriptor *getStream( int index )
|
MediaDescriptor *getStream( int index )
|
||||||
{
|
{
|
||||||
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
if ( index < 0 || (unsigned int)index >= mMediaList.size() )
|
||||||
return( 0 );
|
return( 0 );
|
||||||
return( mMediaList[index] );
|
return( mMediaList[index] );
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *generateFormatContext() const;
|
AVFormatContext *generateFormatContext() const;
|
||||||
};
|
};
|
||||||
#if 0
|
#if 0
|
||||||
v=0
|
v=0
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
#ifdef HAVE_SENDFILE4_SUPPORT
|
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = sendfile(out_fd, in_fd, offset, size);
|
err = sendfile(out_fd, in_fd, offset, size);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
#elif HAVE_SENDFILE7_SUPPORT
|
#elif HAVE_SENDFILE7_SUPPORT
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
||||||
int err;
|
int err;
|
||||||
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0);
|
||||||
if (err && errno != EAGAIN)
|
if (err && errno != EAGAIN)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
*offset += size;
|
*offset += size;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#error "Your platform does not support sendfile. Sorry."
|
#error "Your platform does not support sendfile. Sorry."
|
||||||
|
|
|
@ -32,326 +32,326 @@
|
||||||
StreamBase::~StreamBase()
|
StreamBase::~StreamBase()
|
||||||
{
|
{
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
if ( vid_stream )
|
if ( vid_stream )
|
||||||
{
|
{
|
||||||
delete vid_stream;
|
delete vid_stream;
|
||||||
vid_stream = NULL;
|
vid_stream = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
closeComms();
|
closeComms();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::loadMonitor( int monitor_id )
|
bool StreamBase::loadMonitor( int monitor_id )
|
||||||
{
|
{
|
||||||
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
monitor->connect();
|
monitor->connect();
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::checkInitialised()
|
bool StreamBase::checkInitialised()
|
||||||
{
|
{
|
||||||
if ( !monitor )
|
if ( !monitor )
|
||||||
{
|
{
|
||||||
Fatal( "Cannot stream, not initialised" );
|
Fatal( "Cannot stream, not initialised" );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::updateFrameRate( double fps )
|
void StreamBase::updateFrameRate( double fps )
|
||||||
{
|
{
|
||||||
base_fps = fps;
|
base_fps = fps;
|
||||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||||
frame_mod = 1;
|
frame_mod = 1;
|
||||||
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod );
|
||||||
// Min frame repeat?
|
// Min frame repeat?
|
||||||
while( effective_fps > maxfps )
|
while( effective_fps > maxfps )
|
||||||
{
|
{
|
||||||
effective_fps /= 2.0;
|
effective_fps /= 2.0;
|
||||||
frame_mod *= 2;
|
frame_mod *= 2;
|
||||||
}
|
}
|
||||||
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::checkCommandQueue()
|
bool StreamBase::checkCommandQueue()
|
||||||
{
|
{
|
||||||
if ( sd >= 0 )
|
if ( sd >= 0 )
|
||||||
|
{
|
||||||
|
CmdMsg msg;
|
||||||
|
memset( &msg, 0, sizeof(msg) );
|
||||||
|
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
||||||
|
if ( nbytes < 0 )
|
||||||
{
|
{
|
||||||
CmdMsg msg;
|
if ( errno != EAGAIN )
|
||||||
memset( &msg, 0, sizeof(msg) );
|
{
|
||||||
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 );
|
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
||||||
if ( nbytes < 0 )
|
}
|
||||||
{
|
|
||||||
if ( errno != EAGAIN )
|
|
||||||
{
|
|
||||||
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//else if ( (nbytes != sizeof(msg)) )
|
|
||||||
//{
|
|
||||||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
|
||||||
//}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processCommand( &msg );
|
|
||||||
return( true );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return( false );
|
//else if ( (nbytes != sizeof(msg)) )
|
||||||
|
//{
|
||||||
|
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||||
|
//}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processCommand( &msg );
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *StreamBase::prepareImage( Image *image )
|
Image *StreamBase::prepareImage( Image *image )
|
||||||
{
|
{
|
||||||
static int last_scale = 0;
|
static int last_scale = 0;
|
||||||
static int last_zoom = 0;
|
static int last_zoom = 0;
|
||||||
static int last_x = 0;
|
static int last_x = 0;
|
||||||
static int last_y = 0;
|
static int last_y = 0;
|
||||||
|
|
||||||
if ( !last_scale )
|
if ( !last_scale )
|
||||||
last_scale = scale;
|
|
||||||
if ( !last_zoom )
|
|
||||||
last_zoom = zoom;
|
|
||||||
|
|
||||||
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
|
||||||
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
|
||||||
bool optimisedScaling = false;
|
|
||||||
|
|
||||||
bool image_copied = false;
|
|
||||||
|
|
||||||
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
|
||||||
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
|
||||||
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
|
||||||
|
|
||||||
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
|
||||||
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
|
||||||
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
|
||||||
|
|
||||||
int base_image_width = image->Width(), base_image_height = image->Height();
|
|
||||||
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
|
||||||
|
|
||||||
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
|
||||||
|
|
||||||
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
|
||||||
|
|
||||||
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
|
||||||
|
|
||||||
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
|
||||||
|
|
||||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
|
||||||
|
|
||||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
|
||||||
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
|
||||||
|
|
||||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
|
||||||
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
|
||||||
|
|
||||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
|
||||||
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
|
||||||
|
|
||||||
if ( mag != ZM_SCALE_BASE )
|
|
||||||
{
|
|
||||||
if ( act_mag != ZM_SCALE_BASE )
|
|
||||||
{
|
|
||||||
Debug( 3, "Magnifying by %d", mag );
|
|
||||||
if ( !image_copied )
|
|
||||||
{
|
|
||||||
static Image copy_image;
|
|
||||||
copy_image.Assign( *image );
|
|
||||||
image = ©_image;
|
|
||||||
image_copied = true;
|
|
||||||
}
|
|
||||||
image->Scale( mag );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
|
||||||
|
|
||||||
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
|
||||||
{
|
|
||||||
static Box last_crop;
|
|
||||||
|
|
||||||
if ( mag != last_mag || x != last_x || y != last_y )
|
|
||||||
{
|
|
||||||
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
|
||||||
|
|
||||||
//if ( !last_mag )
|
|
||||||
//last_mag = mag;
|
|
||||||
|
|
||||||
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
|
||||||
last_crop = Box();
|
|
||||||
|
|
||||||
Debug( 3, "Recalculating crop" );
|
|
||||||
// Recalculate crop parameters, as %ges
|
|
||||||
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
|
||||||
click_x += ( x * 100 ) / last_virt_image_width;
|
|
||||||
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
|
||||||
click_y += ( y * 100 ) / last_virt_image_height;
|
|
||||||
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
|
||||||
|
|
||||||
// Convert the click locations to the current image pixels
|
|
||||||
click_x = ( click_x * act_image_width ) / 100;
|
|
||||||
click_y = ( click_y * act_image_height ) / 100;
|
|
||||||
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
|
||||||
|
|
||||||
int lo_x = click_x - (send_image_width/2);
|
|
||||||
if ( lo_x < 0 )
|
|
||||||
lo_x = 0;
|
|
||||||
int hi_x = lo_x + (send_image_width-1);
|
|
||||||
if ( hi_x >= act_image_width )
|
|
||||||
{
|
|
||||||
hi_x = act_image_width - 1;
|
|
||||||
lo_x = hi_x - (send_image_width - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int lo_y = click_y - (send_image_height/2);
|
|
||||||
if ( lo_y < 0 )
|
|
||||||
lo_y = 0;
|
|
||||||
int hi_y = lo_y + (send_image_height-1);
|
|
||||||
if ( hi_y >= act_image_height )
|
|
||||||
{
|
|
||||||
hi_y = act_image_height - 1;
|
|
||||||
lo_y = hi_y - (send_image_height - 1);
|
|
||||||
}
|
|
||||||
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
|
||||||
}
|
|
||||||
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
|
||||||
if ( !image_copied )
|
|
||||||
{
|
|
||||||
static Image copy_image;
|
|
||||||
copy_image.Assign( *image );
|
|
||||||
image = ©_image;
|
|
||||||
image_copied = true;
|
|
||||||
}
|
|
||||||
image->Crop( last_crop );
|
|
||||||
}
|
|
||||||
last_scale = scale;
|
last_scale = scale;
|
||||||
|
if ( !last_zoom )
|
||||||
last_zoom = zoom;
|
last_zoom = zoom;
|
||||||
last_x = x;
|
|
||||||
last_y = y;
|
|
||||||
|
|
||||||
return( image );
|
// Do not bother to scale zoomed in images, just crop them and let the browser scale
|
||||||
|
// Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream.
|
||||||
|
bool optimisedScaling = false;
|
||||||
|
|
||||||
|
bool image_copied = false;
|
||||||
|
|
||||||
|
int mag = (scale * zoom) / ZM_SCALE_BASE;
|
||||||
|
int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag;
|
||||||
|
Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag );
|
||||||
|
|
||||||
|
int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE;
|
||||||
|
int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag;
|
||||||
|
Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag );
|
||||||
|
|
||||||
|
int base_image_width = image->Width(), base_image_height = image->Height();
|
||||||
|
Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height );
|
||||||
|
|
||||||
|
int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height );
|
||||||
|
|
||||||
|
int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height );
|
||||||
|
|
||||||
|
int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height );
|
||||||
|
|
||||||
|
int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height );
|
||||||
|
|
||||||
|
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height );
|
||||||
|
|
||||||
|
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||||
|
Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height );
|
||||||
|
|
||||||
|
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||||
|
Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height );
|
||||||
|
|
||||||
|
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
||||||
|
Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height );
|
||||||
|
|
||||||
|
if ( mag != ZM_SCALE_BASE )
|
||||||
|
{
|
||||||
|
if ( act_mag != ZM_SCALE_BASE )
|
||||||
|
{
|
||||||
|
Debug( 3, "Magnifying by %d", mag );
|
||||||
|
if ( !image_copied )
|
||||||
|
{
|
||||||
|
static Image copy_image;
|
||||||
|
copy_image.Assign( *image );
|
||||||
|
image = ©_image;
|
||||||
|
image_copied = true;
|
||||||
|
}
|
||||||
|
image->Scale( mag );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() );
|
||||||
|
|
||||||
|
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height )
|
||||||
|
{
|
||||||
|
static Box last_crop;
|
||||||
|
|
||||||
|
if ( mag != last_mag || x != last_x || y != last_y )
|
||||||
|
{
|
||||||
|
Debug( 3, "Got click at %d,%d x %d", x, y, mag );
|
||||||
|
|
||||||
|
//if ( !last_mag )
|
||||||
|
//last_mag = mag;
|
||||||
|
|
||||||
|
if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) )
|
||||||
|
last_crop = Box();
|
||||||
|
|
||||||
|
Debug( 3, "Recalculating crop" );
|
||||||
|
// Recalculate crop parameters, as %ges
|
||||||
|
int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image
|
||||||
|
click_x += ( x * 100 ) / last_virt_image_width;
|
||||||
|
int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image
|
||||||
|
click_y += ( y * 100 ) / last_virt_image_height;
|
||||||
|
Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y );
|
||||||
|
|
||||||
|
// Convert the click locations to the current image pixels
|
||||||
|
click_x = ( click_x * act_image_width ) / 100;
|
||||||
|
click_y = ( click_y * act_image_height ) / 100;
|
||||||
|
Debug( 3, "Got readjusted click at %d,%d", click_x, click_y );
|
||||||
|
|
||||||
|
int lo_x = click_x - (send_image_width/2);
|
||||||
|
if ( lo_x < 0 )
|
||||||
|
lo_x = 0;
|
||||||
|
int hi_x = lo_x + (send_image_width-1);
|
||||||
|
if ( hi_x >= act_image_width )
|
||||||
|
{
|
||||||
|
hi_x = act_image_width - 1;
|
||||||
|
lo_x = hi_x - (send_image_width - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lo_y = click_y - (send_image_height/2);
|
||||||
|
if ( lo_y < 0 )
|
||||||
|
lo_y = 0;
|
||||||
|
int hi_y = lo_y + (send_image_height-1);
|
||||||
|
if ( hi_y >= act_image_height )
|
||||||
|
{
|
||||||
|
hi_y = act_image_height - 1;
|
||||||
|
lo_y = hi_y - (send_image_height - 1);
|
||||||
|
}
|
||||||
|
last_crop = Box( lo_x, lo_y, hi_x, hi_y );
|
||||||
|
}
|
||||||
|
Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() );
|
||||||
|
if ( !image_copied )
|
||||||
|
{
|
||||||
|
static Image copy_image;
|
||||||
|
copy_image.Assign( *image );
|
||||||
|
image = ©_image;
|
||||||
|
image_copied = true;
|
||||||
|
}
|
||||||
|
image->Crop( last_crop );
|
||||||
|
}
|
||||||
|
last_scale = scale;
|
||||||
|
last_zoom = zoom;
|
||||||
|
last_x = x;
|
||||||
|
last_y = y;
|
||||||
|
|
||||||
|
return( image );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StreamBase::sendTextFrame( const char *frame_text )
|
bool StreamBase::sendTextFrame( const char *frame_text )
|
||||||
{
|
{
|
||||||
Debug( 2, "Sending text frame '%s'", frame_text );
|
Debug( 2, "Sending text frame '%s'", frame_text );
|
||||||
|
|
||||||
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() );
|
||||||
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
image.Annotate( frame_text, image.centreCoord( frame_text ) );
|
||||||
|
|
||||||
if ( scale != 100 )
|
if ( scale != 100 )
|
||||||
{
|
{
|
||||||
image.Scale( scale );
|
image.Scale( scale );
|
||||||
}
|
}
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
if ( type == STREAM_MPEG )
|
if ( type == STREAM_MPEG )
|
||||||
|
{
|
||||||
|
if ( !vid_stream )
|
||||||
{
|
{
|
||||||
if ( !vid_stream )
|
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
||||||
{
|
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() );
|
vid_stream->OpenStream();
|
||||||
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
|
||||||
vid_stream->OpenStream();
|
|
||||||
}
|
|
||||||
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
|
||||||
}
|
}
|
||||||
else
|
/* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
|
||||||
|
}
|
||||||
|
else
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
{
|
||||||
|
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
int n_bytes = 0;
|
||||||
|
|
||||||
|
image.EncodeJpeg( buffer, &n_bytes );
|
||||||
|
|
||||||
|
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
||||||
|
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
||||||
|
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
||||||
{
|
{
|
||||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
||||||
int n_bytes = 0;
|
return( false );
|
||||||
|
|
||||||
image.EncodeJpeg( buffer, &n_bytes );
|
|
||||||
|
|
||||||
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
|
||||||
fprintf( stdout, "Content-Length: %d\r\n", n_bytes );
|
|
||||||
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" );
|
|
||||||
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 )
|
|
||||||
{
|
|
||||||
Error( "Unable to send stream text frame: %s", strerror(errno) );
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
fprintf( stdout, "\r\n\r\n" );
|
|
||||||
fflush( stdout );
|
|
||||||
}
|
}
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
return( true );
|
fflush( stdout );
|
||||||
|
}
|
||||||
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::openComms()
|
void StreamBase::openComms()
|
||||||
{
|
{
|
||||||
if ( connkey > 0 )
|
if ( connkey > 0 )
|
||||||
|
{
|
||||||
|
|
||||||
|
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
||||||
|
|
||||||
|
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||||
|
if ( lock_fd <= 0 )
|
||||||
{
|
{
|
||||||
|
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||||
snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey);
|
lock_fd = 0;
|
||||||
|
|
||||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
|
||||||
if ( lock_fd <= 0 )
|
|
||||||
{
|
|
||||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
|
||||||
lock_fd = 0;
|
|
||||||
}
|
|
||||||
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
|
||||||
{
|
|
||||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
|
||||||
|
|
||||||
close(lock_fd);
|
|
||||||
lock_fd = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
|
||||||
if ( sd < 0 )
|
|
||||||
{
|
|
||||||
Fatal( "Can't create socket: %s", strerror(errno) );
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
|
||||||
unlink( loc_sock_path );
|
|
||||||
|
|
||||||
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
|
||||||
loc_addr.sun_family = AF_UNIX;
|
|
||||||
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
|
||||||
{
|
|
||||||
Fatal( "Can't bind: %s", strerror(errno) );
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
|
||||||
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
|
||||||
rem_addr.sun_family = AF_UNIX;
|
|
||||||
}
|
}
|
||||||
|
else if ( flock(lock_fd, LOCK_EX) != 0 )
|
||||||
|
{
|
||||||
|
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) );
|
||||||
|
|
||||||
|
close(lock_fd);
|
||||||
|
lock_fd = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sd = socket( AF_UNIX, SOCK_DGRAM, 0 );
|
||||||
|
if ( sd < 0 )
|
||||||
|
{
|
||||||
|
Fatal( "Can't create socket: %s", strerror(errno) );
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey );
|
||||||
|
unlink( loc_sock_path );
|
||||||
|
|
||||||
|
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) );
|
||||||
|
loc_addr.sun_family = AF_UNIX;
|
||||||
|
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 )
|
||||||
|
{
|
||||||
|
Fatal( "Can't bind: %s", strerror(errno) );
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey );
|
||||||
|
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) );
|
||||||
|
rem_addr.sun_family = AF_UNIX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamBase::closeComms()
|
void StreamBase::closeComms()
|
||||||
{
|
{
|
||||||
if ( connkey > 0 )
|
if ( connkey > 0 )
|
||||||
|
{
|
||||||
|
if ( sd >= 0 )
|
||||||
{
|
{
|
||||||
if ( sd >= 0 )
|
close( sd );
|
||||||
{
|
sd = -1;
|
||||||
close( sd );
|
|
||||||
sd = -1;
|
|
||||||
}
|
|
||||||
if ( loc_sock_path[0] )
|
|
||||||
{
|
|
||||||
unlink( loc_sock_path );
|
|
||||||
}
|
|
||||||
if (lock_fd > 0)
|
|
||||||
{
|
|
||||||
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
|
||||||
unlink(sock_path_lock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if ( loc_sock_path[0] )
|
||||||
|
{
|
||||||
|
unlink( loc_sock_path );
|
||||||
|
}
|
||||||
|
if (lock_fd > 0)
|
||||||
|
{
|
||||||
|
close(lock_fd); //close it rather than unlock it incase it got deleted.
|
||||||
|
unlink(sock_path_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
220
src/zm_stream.h
220
src/zm_stream.h
|
@ -33,149 +33,149 @@ class Monitor;
|
||||||
class StreamBase
|
class StreamBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||||
|
|
||||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||||
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
enum { DEFAULT_SCALE=ZM_SCALE_BASE };
|
||||||
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
enum { DEFAULT_ZOOM=ZM_SCALE_BASE };
|
||||||
enum { DEFAULT_MAXFPS=10 };
|
enum { DEFAULT_MAXFPS=10 };
|
||||||
enum { DEFAULT_BITRATE=100000 };
|
enum { DEFAULT_BITRATE=100000 };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int msg_type;
|
int msg_type;
|
||||||
char msg_data[16];
|
char msg_data[16];
|
||||||
} CmdMsg;
|
} CmdMsg;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int msg_type;
|
int msg_type;
|
||||||
char msg_data[256];
|
char msg_data[256];
|
||||||
} DataMsg;
|
} DataMsg;
|
||||||
|
|
||||||
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
|
||||||
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Monitor *monitor;
|
Monitor *monitor;
|
||||||
|
|
||||||
StreamType type;
|
StreamType type;
|
||||||
const char *format;
|
const char *format;
|
||||||
int replay_rate;
|
int replay_rate;
|
||||||
int scale;
|
int scale;
|
||||||
int zoom;
|
int zoom;
|
||||||
double maxfps;
|
double maxfps;
|
||||||
int bitrate;
|
int bitrate;
|
||||||
unsigned short x, y;
|
unsigned short x, y;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int connkey;
|
int connkey;
|
||||||
int sd;
|
int sd;
|
||||||
char loc_sock_path[PATH_MAX];
|
char loc_sock_path[PATH_MAX];
|
||||||
struct sockaddr_un loc_addr;
|
struct sockaddr_un loc_addr;
|
||||||
char rem_sock_path[PATH_MAX];
|
char rem_sock_path[PATH_MAX];
|
||||||
struct sockaddr_un rem_addr;
|
struct sockaddr_un rem_addr;
|
||||||
char sock_path_lock[PATH_MAX];
|
char sock_path_lock[PATH_MAX];
|
||||||
int lock_fd;
|
int lock_fd;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool paused;
|
bool paused;
|
||||||
int step;
|
int step;
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
|
||||||
double base_fps;
|
double base_fps;
|
||||||
double effective_fps;
|
double effective_fps;
|
||||||
int frame_mod;
|
int frame_mod;
|
||||||
|
|
||||||
double last_frame_sent;
|
double last_frame_sent;
|
||||||
struct timeval last_frame_timestamp;
|
struct timeval last_frame_timestamp;
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
VideoStream *vid_stream;
|
VideoStream *vid_stream;
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
||||||
CmdMsg msg;
|
CmdMsg msg;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loadMonitor( int monitor_id );
|
bool loadMonitor( int monitor_id );
|
||||||
bool checkInitialised();
|
bool checkInitialised();
|
||||||
void updateFrameRate( double fps );
|
void updateFrameRate( double fps );
|
||||||
Image *prepareImage( Image *image );
|
Image *prepareImage( Image *image );
|
||||||
bool sendTextFrame( const char *text );
|
bool sendTextFrame( const char *text );
|
||||||
bool checkCommandQueue();
|
bool checkCommandQueue();
|
||||||
virtual void processCommand( const CmdMsg *msg )=0;
|
virtual void processCommand( const CmdMsg *msg )=0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamBase()
|
StreamBase()
|
||||||
{
|
{
|
||||||
monitor = 0;
|
monitor = 0;
|
||||||
|
|
||||||
type = DEFAULT_TYPE;
|
type = DEFAULT_TYPE;
|
||||||
format = "";
|
format = "";
|
||||||
replay_rate = DEFAULT_RATE;
|
replay_rate = DEFAULT_RATE;
|
||||||
scale = DEFAULT_SCALE;
|
scale = DEFAULT_SCALE;
|
||||||
zoom = DEFAULT_ZOOM;
|
zoom = DEFAULT_ZOOM;
|
||||||
maxfps = DEFAULT_MAXFPS;
|
maxfps = DEFAULT_MAXFPS;
|
||||||
bitrate = DEFAULT_BITRATE;
|
bitrate = DEFAULT_BITRATE;
|
||||||
|
|
||||||
paused = false;
|
paused = false;
|
||||||
step = 0;
|
step = 0;
|
||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
|
|
||||||
connkey = 0;
|
connkey = 0;
|
||||||
sd = -1;
|
sd = -1;
|
||||||
lock_fd = 0;
|
lock_fd = 0;
|
||||||
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
|
||||||
memset( &loc_addr, 0, sizeof(loc_addr) );
|
memset( &loc_addr, 0, sizeof(loc_addr) );
|
||||||
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
|
||||||
memset( &rem_addr, 0, sizeof(rem_addr) );
|
memset( &rem_addr, 0, sizeof(rem_addr) );
|
||||||
|
|
||||||
base_fps = 0.0;
|
base_fps = 0.0;
|
||||||
effective_fps = 0.0;
|
effective_fps = 0.0;
|
||||||
frame_mod = 1;
|
frame_mod = 1;
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
vid_stream = 0;
|
vid_stream = 0;
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
}
|
}
|
||||||
virtual ~StreamBase();
|
virtual ~StreamBase();
|
||||||
|
|
||||||
void setStreamType( StreamType p_type )
|
void setStreamType( StreamType p_type )
|
||||||
{
|
{
|
||||||
type = p_type;
|
type = p_type;
|
||||||
}
|
}
|
||||||
void setStreamFormat( const char *p_format )
|
void setStreamFormat( const char *p_format )
|
||||||
{
|
{
|
||||||
format = p_format;
|
format = p_format;
|
||||||
}
|
}
|
||||||
void setStreamScale( int p_scale )
|
void setStreamScale( int p_scale )
|
||||||
{
|
{
|
||||||
scale = p_scale;
|
scale = p_scale;
|
||||||
}
|
}
|
||||||
void setStreamReplayRate( int p_rate )
|
void setStreamReplayRate( int p_rate )
|
||||||
{
|
{
|
||||||
replay_rate = p_rate;
|
replay_rate = p_rate;
|
||||||
}
|
}
|
||||||
void setStreamMaxFPS( double p_maxfps )
|
void setStreamMaxFPS( double p_maxfps )
|
||||||
{
|
{
|
||||||
maxfps = p_maxfps;
|
maxfps = p_maxfps;
|
||||||
}
|
}
|
||||||
void setStreamBitrate( int p_bitrate )
|
void setStreamBitrate( int p_bitrate )
|
||||||
{
|
{
|
||||||
bitrate = p_bitrate;
|
bitrate = p_bitrate;
|
||||||
}
|
}
|
||||||
void setStreamQueue( int p_connkey )
|
void setStreamQueue( int p_connkey )
|
||||||
{
|
{
|
||||||
connkey = p_connkey;
|
connkey = p_connkey;
|
||||||
}
|
}
|
||||||
virtual void openComms();
|
virtual void openComms();
|
||||||
virtual void closeComms();
|
virtual void closeComms();
|
||||||
virtual void runStream()=0;
|
virtual void runStream()=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_STREAM_H
|
#endif // ZM_STREAM_H
|
||||||
|
|
|
@ -29,310 +29,310 @@
|
||||||
|
|
||||||
struct timespec getTimeout( int secs )
|
struct timespec getTimeout( int secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout;
|
struct timespec timeout;
|
||||||
struct timeval temp_timeout;
|
struct timeval temp_timeout;
|
||||||
gettimeofday( &temp_timeout, 0 );
|
gettimeofday( &temp_timeout, 0 );
|
||||||
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
timeout.tv_sec = temp_timeout.tv_sec + secs;
|
||||||
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
timeout.tv_nsec = temp_timeout.tv_usec*1000;
|
||||||
return( timeout );
|
return( timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec getTimeout( double secs )
|
struct timespec getTimeout( double secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout;
|
struct timespec timeout;
|
||||||
struct timeval temp_timeout;
|
struct timeval temp_timeout;
|
||||||
gettimeofday( &temp_timeout, 0 );
|
gettimeofday( &temp_timeout, 0 );
|
||||||
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
timeout.tv_sec = temp_timeout.tv_sec + int(secs);
|
||||||
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs)));
|
||||||
if ( timeout.tv_nsec > 1000000000 )
|
if ( timeout.tv_nsec > 1000000000 )
|
||||||
{
|
{
|
||||||
timeout.tv_sec += 1;
|
timeout.tv_sec += 1;
|
||||||
timeout.tv_nsec -= 1000000000;
|
timeout.tv_nsec -= 1000000000;
|
||||||
}
|
}
|
||||||
return( timeout );
|
return( timeout );
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Mutex()
|
Mutex::Mutex()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
if ( pthread_mutex_init( &mMutex, NULL ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::~Mutex()
|
Mutex::~Mutex()
|
||||||
{
|
{
|
||||||
if ( locked() )
|
if ( locked() )
|
||||||
Warning( "Destroying mutex when locked" );
|
Warning( "Destroying mutex when locked" );
|
||||||
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
if ( pthread_mutex_destroy( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock()
|
void Mutex::lock()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
if ( pthread_mutex_lock( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock( int secs )
|
void Mutex::lock( int secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::lock( double secs )
|
void Mutex::lock( double secs )
|
||||||
{
|
{
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::unlock()
|
void Mutex::unlock()
|
||||||
{
|
{
|
||||||
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
if ( pthread_mutex_unlock( &mMutex ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mutex::locked()
|
bool Mutex::locked()
|
||||||
{
|
{
|
||||||
int state = pthread_mutex_trylock( &mMutex );
|
int state = pthread_mutex_trylock( &mMutex );
|
||||||
if ( state != 0 && state != EBUSY )
|
if ( state != 0 && state != EBUSY )
|
||||||
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) );
|
||||||
if ( state != EBUSY )
|
if ( state != EBUSY )
|
||||||
unlock();
|
unlock();
|
||||||
return( state == EBUSY );
|
return( state == EBUSY );
|
||||||
}
|
}
|
||||||
|
|
||||||
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
Condition::Condition( Mutex &mutex ) : mMutex( mutex )
|
||||||
{
|
{
|
||||||
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
if ( pthread_cond_init( &mCondition, NULL ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
Condition::~Condition()
|
Condition::~Condition()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
if ( pthread_cond_destroy( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::wait()
|
void Condition::wait()
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( int secs )
|
bool Condition::wait( int secs )
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
Debug( 8, "Waiting for %d seconds", secs );
|
Debug( 8, "Waiting for %d seconds", secs );
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||||
return( errno != ETIMEDOUT );
|
return( errno != ETIMEDOUT );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Condition::wait( double secs )
|
bool Condition::wait( double secs )
|
||||||
{
|
{
|
||||||
// Locking done outside of this function
|
// Locking done outside of this function
|
||||||
struct timespec timeout = getTimeout( secs );
|
struct timespec timeout = getTimeout( secs );
|
||||||
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT )
|
||||||
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) );
|
||||||
return( errno != ETIMEDOUT );
|
return( errno != ETIMEDOUT );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::signal()
|
void Condition::signal()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_signal( &mCondition ) < 0 )
|
if ( pthread_cond_signal( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Condition::broadcast()
|
void Condition::broadcast()
|
||||||
{
|
{
|
||||||
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
if ( pthread_cond_broadcast( &mCondition ) < 0 )
|
||||||
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getValue() const
|
template <class T> const T ThreadData<T>::getValue() const
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> T ThreadData<T>::setValue( const T value )
|
template <class T> T ThreadData<T>::setValue( const T value )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
const T valueCopy = mValue = value;
|
const T valueCopy = mValue = value;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
template <class T> const T ThreadData<T>::getUpdatedValue() const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %p", this );
|
Debug( 8, "Waiting for value update, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait();
|
mCondition.wait();
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
template <class T> const T ThreadData<T>::getUpdatedValue( double secs ) const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait( secs );
|
mCondition.wait( secs );
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
template <class T> const T ThreadData<T>::getUpdatedValue( int secs ) const
|
||||||
{
|
{
|
||||||
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
Debug( 8, "Waiting for value update, %d secs, %p", secs, this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
//do {
|
//do {
|
||||||
mCondition.wait( secs );
|
mCondition.wait( secs );
|
||||||
//} while ( !mChanged );
|
//} while ( !mChanged );
|
||||||
const T valueCopy = mValue;
|
const T valueCopy = mValue;
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Got value update, %p", this );
|
Debug( 9, "Got value update, %p", this );
|
||||||
return( valueCopy );
|
return( valueCopy );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
template <class T> void ThreadData<T>::updateValueSignal( const T value )
|
||||||
{
|
{
|
||||||
Debug( 8, "Updating value with signal, %p", this );
|
Debug( 8, "Updating value with signal, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mValue = value;
|
mValue = value;
|
||||||
mChanged = true;
|
mChanged = true;
|
||||||
mCondition.signal();
|
mCondition.signal();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Updated value, %p", this );
|
Debug( 9, "Updated value, %p", this );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
template <class T> void ThreadData<T>::updateValueBroadcast( const T value )
|
||||||
{
|
{
|
||||||
Debug( 8, "Updating value with broadcast, %p", this );
|
Debug( 8, "Updating value with broadcast, %p", this );
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
mValue = value;
|
mValue = value;
|
||||||
mChanged = true;
|
mChanged = true;
|
||||||
mCondition.broadcast();
|
mCondition.broadcast();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
Debug( 9, "Updated value, %p", this );
|
Debug( 9, "Updated value, %p", this );
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::Thread() :
|
Thread::Thread() :
|
||||||
mThreadCondition( mThreadMutex ),
|
mThreadCondition( mThreadMutex ),
|
||||||
mPid( -1 ),
|
mPid( -1 ),
|
||||||
mStarted( false ),
|
mStarted( false ),
|
||||||
mRunning( false )
|
mRunning( false )
|
||||||
{
|
{
|
||||||
Debug( 1, "Creating thread" );
|
Debug( 1, "Creating thread" );
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::~Thread()
|
Thread::~Thread()
|
||||||
{
|
{
|
||||||
Debug( 1, "Destroying thread %d", mPid );
|
Debug( 1, "Destroying thread %d", mPid );
|
||||||
if ( mStarted )
|
if ( mStarted )
|
||||||
join();
|
join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *Thread::mThreadFunc( void *arg )
|
void *Thread::mThreadFunc( void *arg )
|
||||||
{
|
{
|
||||||
Debug( 2, "Invoking thread" );
|
Debug( 2, "Invoking thread" );
|
||||||
|
|
||||||
Thread *thisPtr = (Thread *)arg;
|
Thread *thisPtr = (Thread *)arg;
|
||||||
thisPtr->status = 0;
|
thisPtr->status = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
thisPtr->mThreadMutex.lock();
|
thisPtr->mThreadMutex.lock();
|
||||||
thisPtr->mPid = thisPtr->id();
|
thisPtr->mPid = thisPtr->id();
|
||||||
thisPtr->mThreadCondition.signal();
|
thisPtr->mThreadCondition.signal();
|
||||||
thisPtr->mThreadMutex.unlock();
|
thisPtr->mThreadMutex.unlock();
|
||||||
thisPtr->mRunning = true;
|
thisPtr->mRunning = true;
|
||||||
thisPtr->status = thisPtr->run();
|
thisPtr->status = thisPtr->run();
|
||||||
thisPtr->mRunning = false;
|
thisPtr->mRunning = false;
|
||||||
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) );
|
||||||
return (void *)&(thisPtr->status);
|
return (void *)&(thisPtr->status);
|
||||||
}
|
}
|
||||||
catch ( const ThreadException &e )
|
catch ( const ThreadException &e )
|
||||||
{
|
{
|
||||||
Error( "%s", e.getMessage().c_str() );
|
Error( "%s", e.getMessage().c_str() );
|
||||||
thisPtr->mRunning = false;
|
thisPtr->mRunning = false;
|
||||||
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
Debug( 2, "Exiting thread after exception, status %p", (void *)-1 );
|
||||||
return (void *)-1;
|
return (void *)-1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::start()
|
void Thread::start()
|
||||||
{
|
{
|
||||||
Debug( 1, "Starting thread" );
|
Debug( 1, "Starting thread" );
|
||||||
if ( isThread() )
|
if ( isThread() )
|
||||||
throw ThreadException( "Can't self start thread" );
|
throw ThreadException( "Can't self start thread" );
|
||||||
mThreadMutex.lock();
|
mThreadMutex.lock();
|
||||||
if ( !mStarted )
|
if ( !mStarted )
|
||||||
{
|
{
|
||||||
pthread_attr_t threadAttrs;
|
pthread_attr_t threadAttrs;
|
||||||
pthread_attr_init( &threadAttrs );
|
pthread_attr_init( &threadAttrs );
|
||||||
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM );
|
||||||
|
|
||||||
mStarted = true;
|
mStarted = true;
|
||||||
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 )
|
||||||
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) );
|
||||||
pthread_attr_destroy( &threadAttrs );
|
pthread_attr_destroy( &threadAttrs );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error( "Attempt to start already running thread %d", mPid );
|
Error( "Attempt to start already running thread %d", mPid );
|
||||||
}
|
}
|
||||||
mThreadCondition.wait();
|
mThreadCondition.wait();
|
||||||
mThreadMutex.unlock();
|
mThreadMutex.unlock();
|
||||||
Debug( 1, "Started thread %d", mPid );
|
Debug( 1, "Started thread %d", mPid );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::join()
|
void Thread::join()
|
||||||
{
|
{
|
||||||
Debug( 1, "Joining thread %d", mPid );
|
Debug( 1, "Joining thread %d", mPid );
|
||||||
if ( isThread() )
|
if ( isThread() )
|
||||||
throw ThreadException( "Can't self join thread" );
|
throw ThreadException( "Can't self join thread" );
|
||||||
mThreadMutex.lock();
|
mThreadMutex.lock();
|
||||||
if ( mPid >= 0 )
|
if ( mPid >= 0 )
|
||||||
|
{
|
||||||
|
if ( mStarted )
|
||||||
{
|
{
|
||||||
if ( mStarted )
|
void *threadStatus = 0;
|
||||||
{
|
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
||||||
void *threadStatus = 0;
|
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
||||||
if ( pthread_join( mThread, &threadStatus ) < 0 )
|
mStarted = false;
|
||||||
throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) );
|
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
||||||
mStarted = false;
|
|
||||||
Debug( 1, "Thread %d exited, status %p", mPid, threadStatus );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Warning( "Attempt to join already finished thread %d", mPid );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Warning( "Attempt to join non-started thread %d", mPid );
|
Warning( "Attempt to join already finished thread %d", mPid );
|
||||||
}
|
}
|
||||||
mThreadMutex.unlock();
|
}
|
||||||
Debug( 1, "Joined thread %d", mPid );
|
else
|
||||||
|
{
|
||||||
|
Warning( "Attempt to join non-started thread %d", mPid );
|
||||||
|
}
|
||||||
|
mThreadMutex.unlock();
|
||||||
|
Debug( 1, "Joined thread %d", mPid );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::kill( int signal )
|
void Thread::kill( int signal )
|
||||||
{
|
{
|
||||||
pthread_kill( mThread, signal );
|
pthread_kill( mThread, signal );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some explicit template instantiations
|
// Some explicit template instantiations
|
||||||
|
|
308
src/zm_thread.h
308
src/zm_thread.h
|
@ -36,27 +36,27 @@ class ThreadException : public Exception
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t pid() {
|
pid_t pid() {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
# else
|
# else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t pid() { return( pthread_self() ); }
|
pthread_t pid() { return( pthread_self() ); }
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Mutex
|
class Mutex
|
||||||
|
@ -64,215 +64,215 @@ class Mutex
|
||||||
friend class Condition;
|
friend class Condition;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t mMutex;
|
pthread_mutex_t mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Mutex();
|
Mutex();
|
||||||
~Mutex();
|
~Mutex();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_mutex_t *getMutex()
|
pthread_mutex_t *getMutex()
|
||||||
{
|
{
|
||||||
return( &mMutex );
|
return( &mMutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void lock();
|
void lock();
|
||||||
void lock( int secs );
|
void lock( int secs );
|
||||||
void lock( double secs );
|
void lock( double secs );
|
||||||
void unlock();
|
void unlock();
|
||||||
bool locked();
|
bool locked();
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScopedMutex
|
class ScopedMutex
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex &mMutex;
|
Mutex &mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
ScopedMutex( Mutex &mutex ) : mMutex( mutex )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
}
|
}
|
||||||
~ScopedMutex()
|
~ScopedMutex()
|
||||||
{
|
{
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScopedMutex( const ScopedMutex & );
|
ScopedMutex( const ScopedMutex & );
|
||||||
};
|
};
|
||||||
|
|
||||||
class Condition
|
class Condition
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex &mMutex;
|
Mutex &mMutex;
|
||||||
pthread_cond_t mCondition;
|
pthread_cond_t mCondition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Condition( Mutex &mutex );
|
Condition( Mutex &mutex );
|
||||||
~Condition();
|
~Condition();
|
||||||
|
|
||||||
void wait();
|
void wait();
|
||||||
bool wait( int secs );
|
bool wait( int secs );
|
||||||
bool wait( double secs );
|
bool wait( double secs );
|
||||||
void signal();
|
void signal();
|
||||||
void broadcast();
|
void broadcast();
|
||||||
};
|
};
|
||||||
|
|
||||||
class Semaphore : public Condition
|
class Semaphore : public Condition
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Mutex mMutex;
|
Mutex mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Semaphore() : Condition( mMutex )
|
Semaphore() : Condition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait()
|
void wait()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::wait();
|
Condition::wait();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
bool wait( int secs )
|
bool wait( int secs )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
bool result = Condition::wait( secs );
|
bool result = Condition::wait( secs );
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
bool wait( double secs )
|
bool wait( double secs )
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
bool result = Condition::wait( secs );
|
bool result = Condition::wait( secs );
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
void signal()
|
void signal()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::signal();
|
Condition::signal();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
void broadcast()
|
void broadcast()
|
||||||
{
|
{
|
||||||
mMutex.lock();
|
mMutex.lock();
|
||||||
Condition::broadcast();
|
Condition::broadcast();
|
||||||
mMutex.unlock();
|
mMutex.unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> class ThreadData
|
template <class T> class ThreadData
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
T mValue;
|
T mValue;
|
||||||
mutable bool mChanged;
|
mutable bool mChanged;
|
||||||
mutable Mutex mMutex;
|
mutable Mutex mMutex;
|
||||||
mutable Condition mCondition;
|
mutable Condition mCondition;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
__attribute__((used)) ThreadData() : mCondition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
//~ThreadData() {}
|
//~ThreadData() {}
|
||||||
|
|
||||||
__attribute__((used)) operator T() const
|
__attribute__((used)) operator T() const
|
||||||
{
|
{
|
||||||
return( getValue() );
|
return( getValue() );
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T operator=( const T value )
|
__attribute__((used)) const T operator=( const T value )
|
||||||
{
|
{
|
||||||
return( setValue( value ) );
|
return( setValue( value ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((used)) const T getValueImmediate() const
|
__attribute__((used)) const T getValueImmediate() const
|
||||||
{
|
{
|
||||||
return( mValue );
|
return( mValue );
|
||||||
}
|
}
|
||||||
__attribute__((used)) T setValueImmediate( const T value )
|
__attribute__((used)) T setValueImmediate( const T value )
|
||||||
{
|
{
|
||||||
return( mValue = value );
|
return( mValue = value );
|
||||||
}
|
}
|
||||||
__attribute__((used)) const T getValue() const;
|
__attribute__((used)) const T getValue() const;
|
||||||
__attribute__((used)) T setValue( const T value );
|
__attribute__((used)) T setValue( const T value );
|
||||||
__attribute__((used)) const T getUpdatedValue() const;
|
__attribute__((used)) const T getUpdatedValue() const;
|
||||||
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
__attribute__((used)) const T getUpdatedValue( double secs ) const;
|
||||||
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
__attribute__((used)) const T getUpdatedValue( int secs ) const;
|
||||||
__attribute__((used)) void updateValueSignal( const T value );
|
__attribute__((used)) void updateValueSignal( const T value );
|
||||||
__attribute__((used)) void updateValueBroadcast( const T value );
|
__attribute__((used)) void updateValueBroadcast( const T value );
|
||||||
};
|
};
|
||||||
|
|
||||||
class Thread
|
class Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef void *(*ThreadFunc)( void * );
|
typedef void *(*ThreadFunc)( void * );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
pthread_t mThread;
|
pthread_t mThread;
|
||||||
|
|
||||||
Mutex mThreadMutex;
|
Mutex mThreadMutex;
|
||||||
Condition mThreadCondition;
|
Condition mThreadCondition;
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t mPid;
|
pid_t mPid;
|
||||||
#else
|
#else
|
||||||
pthread_t mPid;
|
pthread_t mPid;
|
||||||
#endif
|
#endif
|
||||||
bool mStarted;
|
bool mStarted;
|
||||||
bool mRunning;
|
bool mRunning;
|
||||||
int status; // Used in various funcions to get around return a local variable
|
int status; // Used in various funcions to get around return a local variable
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Thread();
|
Thread();
|
||||||
virtual ~Thread();
|
virtual ~Thread();
|
||||||
|
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t id() const
|
pid_t id() const
|
||||||
{
|
{
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
|
|
||||||
#else
|
#else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t id() const
|
pthread_t id() const
|
||||||
{
|
{
|
||||||
return( pthread_self() );
|
return( pthread_self() );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void exit( int p_status = 0 )
|
void exit( int p_status = 0 )
|
||||||
{
|
{
|
||||||
//INFO( "Exiting" );
|
//INFO( "Exiting" );
|
||||||
pthread_exit( (void *)&p_status );
|
pthread_exit( (void *)&p_status );
|
||||||
}
|
}
|
||||||
static void *mThreadFunc( void *arg );
|
static void *mThreadFunc( void *arg );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual int run() = 0;
|
virtual int run() = 0;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void join();
|
void join();
|
||||||
void kill( int signal );
|
void kill( int signal );
|
||||||
bool isThread()
|
bool isThread()
|
||||||
{
|
{
|
||||||
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
|
||||||
}
|
}
|
||||||
bool isStarted() const { return( mStarted ); }
|
bool isStarted() const { return( mStarted ); }
|
||||||
bool isRunning() const { return( mRunning ); }
|
bool isRunning() const { return( mRunning ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_THREAD_H
|
#endif // ZM_THREAD_H
|
||||||
|
|
188
src/zm_time.h
188
src/zm_time.h
|
@ -29,48 +29,48 @@
|
||||||
|
|
||||||
struct DeltaTimeval
|
struct DeltaTimeval
|
||||||
{
|
{
|
||||||
bool positive;
|
bool positive;
|
||||||
unsigned long delta;
|
unsigned long delta;
|
||||||
unsigned long sec;
|
unsigned long sec;
|
||||||
unsigned long fsec;
|
unsigned long fsec;
|
||||||
unsigned long prec;
|
unsigned long prec;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DT_GRAN_1000000 1000000
|
#define DT_GRAN_1000000 1000000
|
||||||
#define DT_PREC_6 DT_GRAN_1000000
|
#define DT_PREC_6 DT_GRAN_1000000
|
||||||
#define DT_GRAN_100000 100000
|
#define DT_GRAN_100000 100000
|
||||||
#define DT_PREC_5 DT_GRAN_100000
|
#define DT_PREC_5 DT_GRAN_100000
|
||||||
#define DT_GRAN_10000 10000
|
#define DT_GRAN_10000 10000
|
||||||
#define DT_PREC_4 DT_GRAN_10000
|
#define DT_PREC_4 DT_GRAN_10000
|
||||||
#define DT_GRAN_1000 1000
|
#define DT_GRAN_1000 1000
|
||||||
#define DT_PREC_3 DT_GRAN_1000
|
#define DT_PREC_3 DT_GRAN_1000
|
||||||
#define DT_GRAN_100 100
|
#define DT_GRAN_100 100
|
||||||
#define DT_PREC_2 DT_GRAN_100
|
#define DT_PREC_2 DT_GRAN_100
|
||||||
#define DT_GRAN_10 10
|
#define DT_GRAN_10 10
|
||||||
#define DT_PREC_1 DT_GRAN_10
|
#define DT_PREC_1 DT_GRAN_10
|
||||||
|
|
||||||
#define DT_MAXGRAN DT_GRAN_1000000
|
#define DT_MAXGRAN DT_GRAN_1000000
|
||||||
|
|
||||||
// This obviously wouldn't work for massive deltas but as it's mostly
|
// This obviously wouldn't work for massive deltas but as it's mostly
|
||||||
// for frames it will only usually be a fraction of a second or so
|
// for frames it will only usually be a fraction of a second or so
|
||||||
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
#define DELTA_TIMEVAL( result, time1, time2, precision ) \
|
||||||
{ \
|
{ \
|
||||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||||
result.positive = (delta>=0); \
|
result.positive = (delta>=0); \
|
||||||
result.delta = abs(delta); \
|
result.delta = abs(delta); \
|
||||||
result.sec = result.delta/(precision); \
|
result.sec = result.delta/(precision); \
|
||||||
result.fsec = result.delta%(precision); \
|
result.fsec = result.delta%(precision); \
|
||||||
result.prec = (precision); \
|
result.prec = (precision); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
#define TIMEVAL_INTERVAL( result, time1, time2, precision ) \
|
||||||
{ \
|
{ \
|
||||||
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \
|
||||||
result.positive = (delta>=0); \
|
result.positive = (delta>=0); \
|
||||||
result.delta = abs(delta); \
|
result.delta = abs(delta); \
|
||||||
result.sec = result.delta/(precision); \
|
result.sec = result.delta/(precision); \
|
||||||
result.fsec = result.delta%(precision); \
|
result.fsec = result.delta%(precision); \
|
||||||
result.prec = (precision); \
|
result.prec = (precision); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define USEC_PER_SEC 1000000
|
#define USEC_PER_SEC 1000000
|
||||||
|
@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t;
|
||||||
|
|
||||||
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
inline int tvDiffUsec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffUsec( struct timeval first )
|
inline int tvDiffUsec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffUsec( first, now ) );
|
return( tvDiffUsec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
inline int tvDiffMsec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvDiffMsec( struct timeval first )
|
inline int tvDiffMsec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffMsec( first, now ) );
|
return( tvDiffMsec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double tvDiffSec( struct timeval first, struct timeval last )
|
inline double tvDiffSec( struct timeval first, struct timeval last )
|
||||||
{
|
{
|
||||||
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double tvDiffSec( struct timeval first )
|
inline double tvDiffSec( struct timeval first )
|
||||||
{
|
{
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
return( tvDiffSec( first, now ) );
|
return( tvDiffSec( first, now ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvZero()
|
inline struct timeval tvZero()
|
||||||
{
|
{
|
||||||
struct timeval t = { 0, 0 };
|
struct timeval t = { 0, 0 };
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvIsZero( const struct timeval t )
|
inline int tvIsZero( const struct timeval t )
|
||||||
{
|
{
|
||||||
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
return( t.tv_sec == 0 && t.tv_usec == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
inline int tvCmp( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
if ( t1.tv_sec < t2.tv_sec )
|
if ( t1.tv_sec < t2.tv_sec )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if ( t1.tv_sec > t2.tv_sec )
|
if ( t1.tv_sec > t2.tv_sec )
|
||||||
return( 1 );
|
return( 1 );
|
||||||
if ( t1.tv_usec < t2.tv_usec )
|
if ( t1.tv_usec < t2.tv_usec )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if ( t1.tv_usec > t2.tv_usec )
|
if ( t1.tv_usec > t2.tv_usec )
|
||||||
return( 1 );
|
return( 1 );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tvEq( struct timeval t1, struct timeval t2 )
|
inline int tvEq( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvNow( void )
|
inline struct timeval tvNow( void )
|
||||||
{
|
{
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
gettimeofday( &t, NULL );
|
gettimeofday( &t, NULL );
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvCheck( struct timeval &t )
|
inline struct timeval tvCheck( struct timeval &t )
|
||||||
{
|
{
|
||||||
if ( t.tv_usec >= USEC_PER_SEC )
|
if ( t.tv_usec >= USEC_PER_SEC )
|
||||||
{
|
{
|
||||||
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec );
|
||||||
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
t.tv_sec += t.tv_usec / USEC_PER_SEC;
|
||||||
t.tv_usec %= USEC_PER_SEC;
|
t.tv_usec %= USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
else if ( t.tv_usec < 0 )
|
else if ( t.tv_usec < 0 )
|
||||||
{
|
{
|
||||||
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec );
|
||||||
t.tv_usec = 0;
|
t.tv_usec = 0;
|
||||||
}
|
}
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add t2 to t1
|
// Add t2 to t1
|
||||||
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
inline struct timeval tvAdd( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
tvCheck(t1);
|
tvCheck(t1);
|
||||||
tvCheck(t2);
|
tvCheck(t2);
|
||||||
t1.tv_sec += t2.tv_sec;
|
t1.tv_sec += t2.tv_sec;
|
||||||
t1.tv_usec += t2.tv_usec;
|
t1.tv_usec += t2.tv_usec;
|
||||||
if ( t1.tv_usec >= USEC_PER_SEC )
|
if ( t1.tv_usec >= USEC_PER_SEC )
|
||||||
{
|
{
|
||||||
t1.tv_sec++;
|
t1.tv_sec++;
|
||||||
t1.tv_usec -= USEC_PER_SEC;
|
t1.tv_usec -= USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
return( t1 );
|
return( t1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subtract t2 from t1
|
// Subtract t2 from t1
|
||||||
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
inline struct timeval tvSub( struct timeval t1, struct timeval t2 )
|
||||||
{
|
{
|
||||||
tvCheck(t1);
|
tvCheck(t1);
|
||||||
tvCheck(t2);
|
tvCheck(t2);
|
||||||
t1.tv_sec -= t2.tv_sec;
|
t1.tv_sec -= t2.tv_sec;
|
||||||
t1.tv_usec -= t2.tv_usec;
|
t1.tv_usec -= t2.tv_usec;
|
||||||
if ( t1.tv_usec < 0 )
|
if ( t1.tv_usec < 0 )
|
||||||
{
|
{
|
||||||
t1.tv_sec--;
|
t1.tv_sec--;
|
||||||
t1.tv_usec += USEC_PER_SEC;
|
t1.tv_usec += USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
return( t1 ) ;
|
return( t1 ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
inline struct timeval tvMake( time_t sec, suseconds_t usec )
|
||||||
{
|
{
|
||||||
struct timeval t;
|
struct timeval t;
|
||||||
t.tv_sec = sec;
|
t.tv_sec = sec;
|
||||||
t.tv_usec = usec;
|
t.tv_usec = usec;
|
||||||
return( t );
|
return( t );
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ZM_TIME_H
|
#endif // ZM_TIME_H
|
||||||
|
|
114
src/zm_timer.cpp
114
src/zm_timer.cpp
|
@ -24,96 +24,96 @@
|
||||||
int Timer::TimerThread::mNextTimerId = 0;
|
int Timer::TimerThread::mNextTimerId = 0;
|
||||||
|
|
||||||
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) :
|
||||||
mTimerId( 0 ),
|
mTimerId( 0 ),
|
||||||
mTimer( timer ),
|
mTimer( timer ),
|
||||||
mDuration( duration ),
|
mDuration( duration ),
|
||||||
mRepeat( repeat ),
|
mRepeat( repeat ),
|
||||||
mReset( false ),
|
mReset( false ),
|
||||||
mExpiryFlag( true )
|
mExpiryFlag( true )
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
mTimerId = mNextTimerId++;
|
mTimerId = mNextTimerId++;
|
||||||
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" );
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::TimerThread::~TimerThread()
|
Timer::TimerThread::~TimerThread()
|
||||||
{
|
{
|
||||||
cancel();
|
cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::TimerThread::cancel()
|
void Timer::TimerThread::cancel()
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
if ( mRunning )
|
if ( mRunning )
|
||||||
{
|
{
|
||||||
Debug( 4, "Cancelling timer %d", mTimerId );
|
Debug( 4, "Cancelling timer %d", mTimerId );
|
||||||
mRepeat = false;
|
mRepeat = false;
|
||||||
mReset = false;
|
mReset = false;
|
||||||
mExpiryFlag.updateValueSignal( false );
|
mExpiryFlag.updateValueSignal( false );
|
||||||
}
|
}
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::TimerThread::reset()
|
void Timer::TimerThread::reset()
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
mAccessMutex.lock();
|
||||||
if ( mRunning )
|
if ( mRunning )
|
||||||
{
|
{
|
||||||
Debug( 4, "Resetting timer" );
|
Debug( 4, "Resetting timer" );
|
||||||
mReset = true;
|
mReset = true;
|
||||||
mExpiryFlag.updateValueSignal( false );
|
mExpiryFlag.updateValueSignal( false );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error( "Attempting to reset expired timer %d", mTimerId );
|
Error( "Attempting to reset expired timer %d", mTimerId );
|
||||||
}
|
}
|
||||||
mAccessMutex.unlock();
|
mAccessMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Timer::TimerThread::run()
|
int Timer::TimerThread::run()
|
||||||
{
|
{
|
||||||
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration );
|
||||||
bool timerExpired = false;
|
bool timerExpired = false;
|
||||||
do
|
do
|
||||||
|
{
|
||||||
|
mAccessMutex.lock();
|
||||||
|
mReset = false;
|
||||||
|
mExpiryFlag.setValue( true );
|
||||||
|
mAccessMutex.unlock();
|
||||||
|
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
||||||
|
mAccessMutex.lock();
|
||||||
|
if ( timerExpired )
|
||||||
{
|
{
|
||||||
mAccessMutex.lock();
|
Debug( 4, "Timer %d expired", mTimerId );
|
||||||
mReset = false;
|
mTimer.expire();
|
||||||
mExpiryFlag.setValue( true );
|
}
|
||||||
mAccessMutex.unlock();
|
else
|
||||||
timerExpired = mExpiryFlag.getUpdatedValue( mDuration );
|
{
|
||||||
mAccessMutex.lock();
|
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
||||||
if ( timerExpired )
|
}
|
||||||
{
|
mAccessMutex.unlock();
|
||||||
Debug( 4, "Timer %d expired", mTimerId );
|
} while ( mRepeat || (mReset && !timerExpired) );
|
||||||
mTimer.expire();
|
return( timerExpired );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" );
|
|
||||||
}
|
|
||||||
mAccessMutex.unlock();
|
|
||||||
} while ( mRepeat || (mReset && !timerExpired) );
|
|
||||||
return( timerExpired );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat )
|
||||||
{
|
{
|
||||||
mTimerThread.start();
|
mTimerThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::~Timer()
|
Timer::~Timer()
|
||||||
{
|
{
|
||||||
//cancel();
|
//cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Timer::cancel()
|
void Timer::Timer::cancel()
|
||||||
{
|
{
|
||||||
mTimerThread.cancel();
|
mTimerThread.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Timer::reset()
|
void Timer::Timer::reset()
|
||||||
{
|
{
|
||||||
mTimerThread.reset();
|
mTimerThread.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
120
src/zm_timer.h
120
src/zm_timer.h
|
@ -30,81 +30,81 @@
|
||||||
class Timer
|
class Timer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
class TimerException : public Exception
|
class TimerException : public Exception
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
#ifndef SOLARIS
|
#ifndef SOLARIS
|
||||||
pid_t pid() {
|
pid_t pid() {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
#ifdef __FreeBSD__
|
#ifdef __FreeBSD__
|
||||||
long lwpid;
|
long lwpid;
|
||||||
thr_self(&lwpid);
|
thr_self(&lwpid);
|
||||||
tid = lwpid;
|
tid = lwpid;
|
||||||
#else
|
#else
|
||||||
#ifdef __FreeBSD_kernel__
|
#ifdef __FreeBSD_kernel__
|
||||||
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id
|
||||||
#else
|
#else
|
||||||
tid=syscall(SYS_gettid);
|
tid=syscall(SYS_gettid);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
pthread_t pid() { return( pthread_self() ); }
|
pthread_t pid() { return( pthread_self() ); }
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) )
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TimerThread : public Thread
|
|
||||||
{
|
{
|
||||||
private:
|
}
|
||||||
typedef ThreadData<bool> ExpiryFlag;
|
};
|
||||||
|
|
||||||
private:
|
class TimerThread : public Thread
|
||||||
static int mNextTimerId;
|
{
|
||||||
|
private:
|
||||||
|
typedef ThreadData<bool> ExpiryFlag;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mTimerId;
|
static int mNextTimerId;
|
||||||
Timer &mTimer;
|
|
||||||
int mDuration;
|
|
||||||
int mRepeat;
|
|
||||||
int mReset;
|
|
||||||
ExpiryFlag mExpiryFlag;
|
|
||||||
Mutex mAccessMutex;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void quit()
|
int mTimerId;
|
||||||
{
|
Timer &mTimer;
|
||||||
cancel();
|
int mDuration;
|
||||||
}
|
int mRepeat;
|
||||||
|
int mReset;
|
||||||
|
ExpiryFlag mExpiryFlag;
|
||||||
|
Mutex mAccessMutex;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
TimerThread( Timer &timer, int timeout, bool repeat );
|
void quit()
|
||||||
~TimerThread();
|
{
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
|
||||||
void cancel();
|
public:
|
||||||
void reset();
|
TimerThread( Timer &timer, int timeout, bool repeat );
|
||||||
int run();
|
~TimerThread();
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TimerThread mTimerThread;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Timer( int timeout, bool repeat=false );
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~Timer();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void expire()=0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void cancel();
|
void cancel();
|
||||||
void reset();
|
void reset();
|
||||||
|
int run();
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TimerThread mTimerThread;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Timer( int timeout, bool repeat=false );
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Timer();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void expire()=0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void cancel();
|
||||||
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_TIMER_H
|
#endif // ZM_TIMER_H
|
||||||
|
|
332
src/zm_user.cpp
332
src/zm_user.cpp
|
@ -29,119 +29,119 @@
|
||||||
|
|
||||||
User::User()
|
User::User()
|
||||||
{
|
{
|
||||||
username[0] = password[0] = 0;
|
username[0] = password[0] = 0;
|
||||||
enabled = false;
|
enabled = false;
|
||||||
stream = events = control = monitors = system = PERM_NONE;
|
stream = events = control = monitors = system = PERM_NONE;
|
||||||
monitor_ids = 0;
|
monitor_ids = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
User::User( MYSQL_ROW &dbrow )
|
User::User( MYSQL_ROW &dbrow )
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
strncpy( username, dbrow[index++], sizeof(username) );
|
strncpy( username, dbrow[index++], sizeof(username) );
|
||||||
strncpy( password, dbrow[index++], sizeof(password) );
|
strncpy( password, dbrow[index++], sizeof(password) );
|
||||||
enabled = (bool)atoi( dbrow[index++] );
|
enabled = (bool)atoi( dbrow[index++] );
|
||||||
stream = (Permission)atoi( dbrow[index++] );
|
stream = (Permission)atoi( dbrow[index++] );
|
||||||
events = (Permission)atoi( dbrow[index++] );
|
events = (Permission)atoi( dbrow[index++] );
|
||||||
control = (Permission)atoi( dbrow[index++] );
|
control = (Permission)atoi( dbrow[index++] );
|
||||||
monitors = (Permission)atoi( dbrow[index++] );
|
monitors = (Permission)atoi( dbrow[index++] );
|
||||||
system = (Permission)atoi( dbrow[index++] );
|
system = (Permission)atoi( dbrow[index++] );
|
||||||
monitor_ids = 0;
|
monitor_ids = 0;
|
||||||
char *monitor_ids_str = dbrow[index++];
|
char *monitor_ids_str = dbrow[index++];
|
||||||
if ( monitor_ids_str && *monitor_ids_str )
|
if ( monitor_ids_str && *monitor_ids_str )
|
||||||
{
|
{
|
||||||
monitor_ids = new int[strlen(monitor_ids_str)];
|
monitor_ids = new int[strlen(monitor_ids_str)];
|
||||||
int n_monitor_ids = 0;
|
int n_monitor_ids = 0;
|
||||||
const char *ptr = monitor_ids_str;
|
const char *ptr = monitor_ids_str;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
int id = 0;
|
int id = 0;
|
||||||
while( isdigit( *ptr ) )
|
while( isdigit( *ptr ) )
|
||||||
{
|
{
|
||||||
id *= 10;
|
id *= 10;
|
||||||
id += *ptr-'0';
|
id += *ptr-'0';
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
if ( id )
|
if ( id )
|
||||||
{
|
{
|
||||||
monitor_ids[n_monitor_ids++] = id;
|
monitor_ids[n_monitor_ids++] = id;
|
||||||
if ( !*ptr )
|
if ( !*ptr )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while ( !isdigit( *ptr ) )
|
while ( !isdigit( *ptr ) )
|
||||||
ptr++;
|
ptr++;
|
||||||
} while( *ptr );
|
} while( *ptr );
|
||||||
monitor_ids[n_monitor_ids] = 0;
|
monitor_ids[n_monitor_ids] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User::~User()
|
User::~User()
|
||||||
{
|
{
|
||||||
delete monitor_ids;
|
delete monitor_ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool User::canAccess( int monitor_id )
|
bool User::canAccess( int monitor_id )
|
||||||
{
|
{
|
||||||
if ( !monitor_ids )
|
if ( !monitor_ids )
|
||||||
{
|
{
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
for ( int i = 0; monitor_ids[i]; i++ )
|
for ( int i = 0; monitor_ids[i]; i++ )
|
||||||
{
|
{
|
||||||
if ( monitor_ids[i] == monitor_id )
|
if ( monitor_ids[i] == monitor_id )
|
||||||
{
|
{
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to load a user from username and password
|
// Function to load a user from username and password
|
||||||
// Please note that in auth relay mode = none, password is NULL
|
// Please note that in auth relay mode = none, password is NULL
|
||||||
User *zmLoadUser( const char *username, const char *password )
|
User *zmLoadUser( const char *username, const char *password )
|
||||||
{
|
{
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
char safer_username[65]; // current db username size is 32
|
char safer_username[65]; // current db username size is 32
|
||||||
char safer_password[129]; // current db password size is 64
|
char safer_password[129]; // current db password size is 64
|
||||||
|
|
||||||
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
// According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator.
|
||||||
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) );
|
||||||
|
|
||||||
if ( password ) {
|
if ( password ) {
|
||||||
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) );
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password );
|
||||||
} else {
|
} else {
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result )
|
if ( !result )
|
||||||
{
|
{
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows( result );
|
||||||
|
|
||||||
if ( n_users != 1 )
|
if ( n_users != 1 )
|
||||||
{
|
{
|
||||||
Warning( "Unable to authenticate user %s", username );
|
Warning( "Unable to authenticate user %s", username );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||||
|
|
||||||
User *user = new User( dbrow );
|
User *user = new User( dbrow );
|
||||||
Info( "Authenticated user '%s'", user->getUsername() );
|
Info( "Authenticated user '%s'", user->getUsername() );
|
||||||
|
|
||||||
mysql_free_result( result );
|
mysql_free_result( result );
|
||||||
|
|
||||||
return( user );
|
return( user );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to validate an authentication string
|
// Function to validate an authentication string
|
||||||
|
@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr )
|
||||||
{
|
{
|
||||||
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
#if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
#ifdef HAVE_GCRYPT_H
|
#ifdef HAVE_GCRYPT_H
|
||||||
// Special initialisation for libgcrypt
|
// Special initialisation for libgcrypt
|
||||||
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
if ( !gcry_check_version( GCRYPT_VERSION ) )
|
||||||
{
|
{
|
||||||
Fatal( "Unable to initialise libgcrypt" );
|
Fatal( "Unable to initialise libgcrypt" );
|
||||||
}
|
}
|
||||||
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
gcry_control( GCRYCTL_DISABLE_SECMEM, 0 );
|
||||||
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 );
|
||||||
#endif // HAVE_GCRYPT_H
|
#endif // HAVE_GCRYPT_H
|
||||||
|
|
||||||
const char *remote_addr = "";
|
const char *remote_addr = "";
|
||||||
if ( use_remote_addr )
|
if ( use_remote_addr )
|
||||||
{
|
{
|
||||||
remote_addr = getenv( "REMOTE_ADDR" );
|
remote_addr = getenv( "REMOTE_ADDR" );
|
||||||
if ( !remote_addr )
|
if ( !remote_addr )
|
||||||
{
|
{
|
||||||
Warning( "Can't determine remote address, using null" );
|
Warning( "Can't determine remote address, using null" );
|
||||||
remote_addr = "";
|
remote_addr = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
Debug( 1, "Attempting to authenticate user from auth string '%s'", auth );
|
||||||
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
char sql[ZM_SQL_SML_BUFSIZ] = "";
|
||||||
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" );
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql ) )
|
if ( mysql_query( &dbconn, sql ) )
|
||||||
{
|
{
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||||
if ( !result )
|
if ( !result )
|
||||||
{
|
{
|
||||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
int n_users = mysql_num_rows( result );
|
int n_users = mysql_num_rows( result );
|
||||||
|
|
||||||
if ( n_users < 1 )
|
if ( n_users < 1 )
|
||||||
{
|
{
|
||||||
Warning( "Unable to authenticate user" );
|
Warning( "Unable to authenticate user" );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
while( MYSQL_ROW dbrow = mysql_fetch_row( result ) )
|
||||||
{
|
{
|
||||||
const char *user = dbrow[0];
|
const char *user = dbrow[0];
|
||||||
const char *pass = dbrow[1];
|
const char *pass = dbrow[1];
|
||||||
|
|
||||||
char auth_key[512] = "";
|
char auth_key[512] = "";
|
||||||
char auth_md5[32+1] = "";
|
char auth_md5[32+1] = "";
|
||||||
size_t md5len = 16;
|
size_t md5len = 16;
|
||||||
unsigned char md5sum[md5len];
|
unsigned char md5sum[md5len];
|
||||||
|
|
||||||
time_t now = time( 0 );
|
time_t now = time( 0 );
|
||||||
int max_tries = 2;
|
int max_tries = 2;
|
||||||
|
|
||||||
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
for ( int i = 0; i < max_tries; i++, now -= (60*60) )
|
||||||
{
|
{
|
||||||
struct tm *now_tm = localtime( &now );
|
struct tm *now_tm = localtime( &now );
|
||||||
|
|
||||||
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d",
|
||||||
config.auth_hash_secret,
|
config.auth_hash_secret,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
remote_addr,
|
remote_addr,
|
||||||
now_tm->tm_hour,
|
now_tm->tm_hour,
|
||||||
now_tm->tm_mday,
|
now_tm->tm_mday,
|
||||||
now_tm->tm_mon,
|
now_tm->tm_mon,
|
||||||
now_tm->tm_year
|
now_tm->tm_year
|
||||||
);
|
);
|
||||||
|
|
||||||
#if HAVE_DECL_MD5
|
#if HAVE_DECL_MD5
|
||||||
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum );
|
||||||
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
#elif HAVE_DECL_GNUTLS_FINGERPRINT
|
||||||
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) };
|
||||||
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len );
|
||||||
#endif
|
#endif
|
||||||
auth_md5[0] = '\0';
|
auth_md5[0] = '\0';
|
||||||
for ( unsigned int j = 0; j < md5len; j++ )
|
for ( unsigned int j = 0; j < md5len; j++ )
|
||||||
{
|
{
|
||||||
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
sprintf( &auth_md5[2*j], "%02x", md5sum[j] );
|
||||||
}
|
}
|
||||||
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 );
|
||||||
|
|
||||||
if ( !strcmp( auth, auth_md5 ) )
|
if ( !strcmp( auth, auth_md5 ) )
|
||||||
{
|
{
|
||||||
// We have a match
|
// We have a match
|
||||||
User *user = new User( dbrow );
|
User *user = new User( dbrow );
|
||||||
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
Debug(1, "Authenticated user '%s'", user->getUsername() );
|
||||||
return( user );
|
return( user );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else // HAVE_DECL_MD5
|
#else // HAVE_DECL_MD5
|
||||||
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
Error( "You need to build with gnutls or openssl installed to use hash based authentication" );
|
||||||
#endif // HAVE_DECL_MD5
|
#endif // HAVE_DECL_MD5
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,33 +39,33 @@
|
||||||
class User
|
class User
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
char username[32+1];
|
char username[32+1];
|
||||||
char password[64+1];
|
char password[64+1];
|
||||||
bool enabled;
|
bool enabled;
|
||||||
Permission stream;
|
Permission stream;
|
||||||
Permission events;
|
Permission events;
|
||||||
Permission control;
|
Permission control;
|
||||||
Permission monitors;
|
Permission monitors;
|
||||||
Permission system;
|
Permission system;
|
||||||
int *monitor_ids;
|
int *monitor_ids;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
User();
|
User();
|
||||||
User( MYSQL_ROW &dbrow );
|
User( MYSQL_ROW &dbrow );
|
||||||
~User();
|
~User();
|
||||||
|
|
||||||
const char *getUsername() const { return( username ); }
|
const char *getUsername() const { return( username ); }
|
||||||
const char *getPassword() const { return( password ); }
|
const char *getPassword() const { return( password ); }
|
||||||
bool isEnabled() const { return( enabled ); }
|
bool isEnabled() const { return( enabled ); }
|
||||||
Permission getStream() const { return( stream ); }
|
Permission getStream() const { return( stream ); }
|
||||||
Permission getEvents() const { return( events ); }
|
Permission getEvents() const { return( events ); }
|
||||||
Permission getControl() const { return( control ); }
|
Permission getControl() const { return( control ); }
|
||||||
Permission getMonitors() const { return( monitors ); }
|
Permission getMonitors() const { return( monitors ); }
|
||||||
Permission getSystem() const { return( system ); }
|
Permission getSystem() const { return( system ); }
|
||||||
bool canAccess( int monitor_id );
|
bool canAccess( int monitor_id );
|
||||||
};
|
};
|
||||||
|
|
||||||
User *zmLoadUser( const char *username, const char *password=0 );
|
User *zmLoadUser( const char *username, const char *password=0 );
|
||||||
|
|
440
src/zm_utils.cpp
440
src/zm_utils.cpp
|
@ -28,256 +28,256 @@
|
||||||
unsigned int sseversion = 0;
|
unsigned int sseversion = 0;
|
||||||
|
|
||||||
std::string trimSet(std::string str, std::string trimset) {
|
std::string trimSet(std::string str, std::string trimset) {
|
||||||
// Trim Both leading and trailing sets
|
// Trim Both leading and trailing sets
|
||||||
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces
|
||||||
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
|
||||||
|
|
||||||
// if all spaces or empty return an empty string
|
// if all spaces or empty return an empty string
|
||||||
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
|
||||||
{
|
{
|
||||||
return std::string("");
|
return std::string("");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return str.substr( startpos, endpos-startpos+1 );
|
return str.substr( startpos, endpos-startpos+1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trimSpaces(std::string str)
|
std::string trimSpaces(std::string str)
|
||||||
{
|
{
|
||||||
return trimSet(str, " \t");
|
return trimSet(str, " \t");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string replaceAll(std::string str, std::string from, std::string to) {
|
std::string replaceAll(std::string str, std::string from, std::string to) {
|
||||||
if(from.empty())
|
if(from.empty())
|
||||||
return str;
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
||||||
str.replace(start_pos, from.length(), to);
|
|
||||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
|
||||||
}
|
|
||||||
return str;
|
return str;
|
||||||
|
size_t start_pos = 0;
|
||||||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||||
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string stringtf( const char *format, ... )
|
const std::string stringtf( const char *format, ... )
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char tempBuffer[8192];
|
char tempBuffer[8192];
|
||||||
std::string tempString;
|
std::string tempString;
|
||||||
|
|
||||||
va_start(ap, format );
|
va_start(ap, format );
|
||||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
tempString = tempBuffer;
|
tempString = tempBuffer;
|
||||||
|
|
||||||
return( tempString );
|
return( tempString );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string stringtf( const std::string &format, ... )
|
const std::string stringtf( const std::string &format, ... )
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char tempBuffer[8192];
|
char tempBuffer[8192];
|
||||||
std::string tempString;
|
std::string tempString;
|
||||||
|
|
||||||
va_start(ap, format );
|
va_start(ap, format );
|
||||||
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
tempString = tempBuffer;
|
tempString = tempBuffer;
|
||||||
|
|
||||||
return( tempString );
|
return( tempString );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool startsWith( const std::string &haystack, const std::string &needle )
|
bool startsWith( const std::string &haystack, const std::string &needle )
|
||||||
{
|
{
|
||||||
return( haystack.substr( 0, needle.length() ) == needle );
|
return( haystack.substr( 0, needle.length() ) == needle );
|
||||||
}
|
}
|
||||||
|
|
||||||
StringVector split( const std::string &string, const std::string chars, int limit )
|
StringVector split( const std::string &string, const std::string chars, int limit )
|
||||||
{
|
{
|
||||||
StringVector stringVector;
|
StringVector stringVector;
|
||||||
std::string tempString = string;
|
std::string tempString = string;
|
||||||
std::string::size_type startIndex = 0;
|
std::string::size_type startIndex = 0;
|
||||||
std::string::size_type endIndex = 0;
|
std::string::size_type endIndex = 0;
|
||||||
|
|
||||||
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
//Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit );
|
||||||
do
|
do
|
||||||
|
{
|
||||||
|
// Find delimiters
|
||||||
|
endIndex = string.find_first_of( chars, startIndex );
|
||||||
|
//Info( "Got endIndex at %d", endIndex );
|
||||||
|
if ( endIndex > 0 )
|
||||||
{
|
{
|
||||||
// Find delimiters
|
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
||||||
endIndex = string.find_first_of( chars, startIndex );
|
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
||||||
//Info( "Got endIndex at %d", endIndex );
|
}
|
||||||
if ( endIndex > 0 )
|
if ( endIndex == std::string::npos )
|
||||||
{
|
break;
|
||||||
//Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() );
|
// Find non-delimiters
|
||||||
stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) );
|
startIndex = tempString.find_first_not_of( chars, endIndex );
|
||||||
}
|
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
||||||
if ( endIndex == std::string::npos )
|
{
|
||||||
break;
|
stringVector.push_back( string.substr( startIndex ) );
|
||||||
// Find non-delimiters
|
break;
|
||||||
startIndex = tempString.find_first_not_of( chars, endIndex );
|
}
|
||||||
if ( limit && (stringVector.size() == (unsigned int)(limit-1)) )
|
//Info( "Got new startIndex at %d", startIndex );
|
||||||
{
|
} while ( startIndex != std::string::npos );
|
||||||
stringVector.push_back( string.substr( startIndex ) );
|
//Info( "Finished with %d strings", stringVector.size() );
|
||||||
break;
|
|
||||||
}
|
|
||||||
//Info( "Got new startIndex at %d", startIndex );
|
|
||||||
} while ( startIndex != std::string::npos );
|
|
||||||
//Info( "Finished with %d strings", stringVector.size() );
|
|
||||||
|
|
||||||
return( stringVector );
|
return( stringVector );
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string join(const StringVector v, const char * delim ) {
|
const std::string join(const StringVector v, const char * delim ) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
for(size_t i = 0; i < v.size(); ++i) {
|
for(size_t i = 0; i < v.size(); ++i) {
|
||||||
if(i != 0)
|
if(i != 0)
|
||||||
ss << ",";
|
ss << ",";
|
||||||
ss << v[i];
|
ss << v[i];
|
||||||
}
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string base64Encode( const std::string &inString )
|
const std::string base64Encode( const std::string &inString )
|
||||||
{
|
{
|
||||||
static char base64_table[64] = { '\0' };
|
static char base64_table[64] = { '\0' };
|
||||||
|
|
||||||
if ( !base64_table[0] )
|
if ( !base64_table[0] )
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for ( char c = 'A'; c <= 'Z'; c++ )
|
for ( char c = 'A'; c <= 'Z'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
for ( char c = 'a'; c <= 'z'; c++ )
|
for ( char c = 'a'; c <= 'z'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
for ( char c = '0'; c <= '9'; c++ )
|
for ( char c = '0'; c <= '9'; c++ )
|
||||||
base64_table[i++] = c;
|
base64_table[i++] = c;
|
||||||
base64_table[i++] = '+';
|
base64_table[i++] = '+';
|
||||||
base64_table[i++] = '/';
|
base64_table[i++] = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string outString;
|
std::string outString;
|
||||||
outString.reserve( 2 * inString.size() );
|
outString.reserve( 2 * inString.size() );
|
||||||
|
|
||||||
const char *inPtr = inString.c_str();
|
const char *inPtr = inString.c_str();
|
||||||
while( *inPtr )
|
while( *inPtr )
|
||||||
{
|
{
|
||||||
unsigned char selection = *inPtr >> 2;
|
unsigned char selection = *inPtr >> 2;
|
||||||
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
unsigned char remainder = (*inPtr++ & 0x03) << 4;
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
|
||||||
if ( *inPtr )
|
if ( *inPtr )
|
||||||
{
|
{
|
||||||
selection = remainder | (*inPtr >> 4);
|
selection = remainder | (*inPtr >> 4);
|
||||||
remainder = (*inPtr++ & 0x0f) << 2;
|
remainder = (*inPtr++ & 0x0f) << 2;
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
|
|
||||||
if ( *inPtr )
|
if ( *inPtr )
|
||||||
{
|
{
|
||||||
selection = remainder | (*inPtr >> 6);
|
selection = remainder | (*inPtr >> 6);
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
selection = (*inPtr++ & 0x3f);
|
selection = (*inPtr++ & 0x3f);
|
||||||
outString += base64_table[selection];
|
outString += base64_table[selection];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outString += base64_table[remainder];
|
outString += base64_table[remainder];
|
||||||
outString += '=';
|
outString += '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outString += base64_table[remainder];
|
outString += base64_table[remainder];
|
||||||
outString += '=';
|
outString += '=';
|
||||||
outString += '=';
|
outString += '=';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( outString );
|
return( outString );
|
||||||
}
|
}
|
||||||
|
|
||||||
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
int split(const char* string, const char delim, std::vector<std::string>& items) {
|
||||||
if(string == NULL)
|
if(string == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(string[0] == 0)
|
if(string[0] == 0)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
std::string str(string);
|
std::string str(string);
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
pos = str.find(delim);
|
pos = str.find(delim);
|
||||||
items.push_back(str.substr(0, pos));
|
items.push_back(str.substr(0, pos));
|
||||||
str.erase(0, pos+1);
|
str.erase(0, pos+1);
|
||||||
|
|
||||||
if(pos == std::string::npos)
|
if(pos == std::string::npos)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.size();
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
|
||||||
if(string == NULL)
|
if(string == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(string[0] == 0)
|
if(string[0] == 0)
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
std::string str(string);
|
std::string str(string);
|
||||||
size_t pos = str.find(delim);
|
size_t pos = str.find(delim);
|
||||||
|
|
||||||
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
if(pos == std::string::npos || pos == 0 || pos >= str.length())
|
||||||
return -3;
|
return -3;
|
||||||
|
|
||||||
name = str.substr(0, pos);
|
name = str.substr(0, pos);
|
||||||
value = str.substr(pos+1, std::string::npos);
|
value = str.substr(pos+1, std::string::npos);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sets sse_version */
|
/* Sets sse_version */
|
||||||
void ssedetect() {
|
void ssedetect() {
|
||||||
#if (defined(__i386__) || defined(__x86_64__))
|
#if (defined(__i386__) || defined(__x86_64__))
|
||||||
/* x86 or x86-64 processor */
|
/* x86 or x86-64 processor */
|
||||||
uint32_t r_edx, r_ecx;
|
uint32_t r_edx, r_ecx;
|
||||||
|
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
"pushl %%ebx;\n\t"
|
"pushl %%ebx;\n\t"
|
||||||
#endif
|
#endif
|
||||||
"mov $0x1,%%eax\n\t"
|
"mov $0x1,%%eax\n\t"
|
||||||
"cpuid\n\t"
|
"cpuid\n\t"
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
"popl %%ebx;\n\t"
|
"popl %%ebx;\n\t"
|
||||||
#endif
|
#endif
|
||||||
: "=d" (r_edx), "=c" (r_ecx)
|
: "=d" (r_edx), "=c" (r_ecx)
|
||||||
:
|
:
|
||||||
: "%eax"
|
: "%eax"
|
||||||
#if !defined(__i386__)
|
#if !defined(__i386__)
|
||||||
, "%ebx"
|
, "%ebx"
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
if (r_ecx & 0x00000200) {
|
if (r_ecx & 0x00000200) {
|
||||||
sseversion = 35; /* SSSE3 */
|
sseversion = 35; /* SSSE3 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
|
||||||
} else if (r_ecx & 0x00000001) {
|
} else if (r_ecx & 0x00000001) {
|
||||||
sseversion = 30; /* SSE3 */
|
sseversion = 30; /* SSE3 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
|
||||||
} else if (r_edx & 0x04000000) {
|
} else if (r_edx & 0x04000000) {
|
||||||
sseversion = 20; /* SSE2 */
|
sseversion = 20; /* SSE2 */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
|
||||||
} else if (r_edx & 0x02000000) {
|
} else if (r_edx & 0x02000000) {
|
||||||
sseversion = 10; /* SSE */
|
sseversion = 10; /* SSE */
|
||||||
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
Debug(1,"Detected a x86\\x86-64 processor with SSE");
|
||||||
} else {
|
} else {
|
||||||
sseversion = 0;
|
sseversion = 0;
|
||||||
Debug(1,"Detected a x86\\x86-64 processor");
|
Debug(1,"Detected a x86\\x86-64 processor");
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* Non x86 or x86-64 processor, SSE2 is not available */
|
/* Non x86 or x86-64 processor, SSE2 is not available */
|
||||||
Debug(1,"Detected a non x86\\x86-64 processor");
|
Debug(1,"Detected a non x86\\x86-64 processor");
|
||||||
sseversion = 0;
|
sseversion = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2")))
|
||||||
#endif
|
#endif
|
||||||
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
|
||||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||||
if(bytes > 128) {
|
if(bytes > 128) {
|
||||||
unsigned int remainder = bytes % 128;
|
unsigned int remainder = bytes % 128;
|
||||||
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
|
||||||
|
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"sse2_copy_iter:\n\t"
|
"sse2_copy_iter:\n\t"
|
||||||
"movdqa (%0),%%xmm0\n\t"
|
"movdqa (%0),%%xmm0\n\t"
|
||||||
"movdqa 0x10(%0),%%xmm1\n\t"
|
"movdqa 0x10(%0),%%xmm1\n\t"
|
||||||
"movdqa 0x20(%0),%%xmm2\n\t"
|
"movdqa 0x20(%0),%%xmm2\n\t"
|
||||||
"movdqa 0x30(%0),%%xmm3\n\t"
|
"movdqa 0x30(%0),%%xmm3\n\t"
|
||||||
"movdqa 0x40(%0),%%xmm4\n\t"
|
"movdqa 0x40(%0),%%xmm4\n\t"
|
||||||
"movdqa 0x50(%0),%%xmm5\n\t"
|
"movdqa 0x50(%0),%%xmm5\n\t"
|
||||||
"movdqa 0x60(%0),%%xmm6\n\t"
|
"movdqa 0x60(%0),%%xmm6\n\t"
|
||||||
"movdqa 0x70(%0),%%xmm7\n\t"
|
"movdqa 0x70(%0),%%xmm7\n\t"
|
||||||
"movntdq %%xmm0,(%1)\n\t"
|
"movntdq %%xmm0,(%1)\n\t"
|
||||||
"movntdq %%xmm1,0x10(%1)\n\t"
|
"movntdq %%xmm1,0x10(%1)\n\t"
|
||||||
"movntdq %%xmm2,0x20(%1)\n\t"
|
"movntdq %%xmm2,0x20(%1)\n\t"
|
||||||
"movntdq %%xmm3,0x30(%1)\n\t"
|
"movntdq %%xmm3,0x30(%1)\n\t"
|
||||||
"movntdq %%xmm4,0x40(%1)\n\t"
|
"movntdq %%xmm4,0x40(%1)\n\t"
|
||||||
"movntdq %%xmm5,0x50(%1)\n\t"
|
"movntdq %%xmm5,0x50(%1)\n\t"
|
||||||
"movntdq %%xmm6,0x60(%1)\n\t"
|
"movntdq %%xmm6,0x60(%1)\n\t"
|
||||||
"movntdq %%xmm7,0x70(%1)\n\t"
|
"movntdq %%xmm7,0x70(%1)\n\t"
|
||||||
"add $0x80, %0\n\t"
|
"add $0x80, %0\n\t"
|
||||||
"add $0x80, %1\n\t"
|
"add $0x80, %1\n\t"
|
||||||
"cmp %2, %0\n\t"
|
"cmp %2, %0\n\t"
|
||||||
"jb sse2_copy_iter\n\t"
|
"jb sse2_copy_iter\n\t"
|
||||||
"test %3, %3\n\t"
|
"test %3, %3\n\t"
|
||||||
"jz sse2_copy_finish\n\t"
|
"jz sse2_copy_finish\n\t"
|
||||||
"cld\n\t"
|
"cld\n\t"
|
||||||
"rep movsb\n\t"
|
"rep movsb\n\t"
|
||||||
"sse2_copy_finish:\n\t"
|
"sse2_copy_finish:\n\t"
|
||||||
:
|
:
|
||||||
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
: "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder)
|
||||||
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
: "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory"
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Standard memcpy */
|
/* Standard memcpy */
|
||||||
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
__asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Non x86\x86-64 platform, use memcpy */
|
/* Non x86\x86-64 platform, use memcpy */
|
||||||
memcpy(dest,src,bytes);
|
memcpy(dest,src,bytes);
|
||||||
#endif
|
#endif
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) {
|
||||||
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
if (((end->tv_nsec)-(start->tv_nsec))<0) {
|
||||||
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
diff->tv_sec = end->tv_sec-start->tv_sec-1;
|
||||||
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec;
|
||||||
} else {
|
} else {
|
||||||
diff->tv_sec = end->tv_sec-start->tv_sec;
|
diff->tv_sec = end->tv_sec-start->tv_sec;
|
||||||
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
diff->tv_nsec = end->tv_nsec-start->tv_nsec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
|
||||||
|
|
||||||
inline int max( int a, int b )
|
inline int max( int a, int b )
|
||||||
{
|
{
|
||||||
return( a>=b?a:b );
|
return( a>=b?a:b );
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int min( int a, int b )
|
inline int min( int a, int b )
|
||||||
{
|
{
|
||||||
return( a<=b?a:b );
|
return( a<=b?a:b );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssedetect();
|
void ssedetect();
|
||||||
|
|
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
|
class Zone
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
struct Range
|
struct Range
|
||||||
{
|
{
|
||||||
int lo_x;
|
int lo_x;
|
||||||
int hi_x;
|
int hi_x;
|
||||||
int off_x;
|
int off_x;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType;
|
||||||
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Inputs
|
// Inputs
|
||||||
Monitor *monitor;
|
Monitor *monitor;
|
||||||
|
|
||||||
int id;
|
int id;
|
||||||
char *label;
|
char *label;
|
||||||
ZoneType type;
|
ZoneType type;
|
||||||
Polygon polygon;
|
Polygon polygon;
|
||||||
Rgb alarm_rgb;
|
Rgb alarm_rgb;
|
||||||
CheckMethod check_method;
|
CheckMethod check_method;
|
||||||
|
|
||||||
int min_pixel_threshold;
|
int min_pixel_threshold;
|
||||||
int max_pixel_threshold;
|
int max_pixel_threshold;
|
||||||
|
|
||||||
int min_alarm_pixels;
|
int min_alarm_pixels;
|
||||||
int max_alarm_pixels;
|
int max_alarm_pixels;
|
||||||
|
|
||||||
Coord filter_box;
|
Coord filter_box;
|
||||||
int min_filter_pixels;
|
int min_filter_pixels;
|
||||||
int max_filter_pixels;
|
int max_filter_pixels;
|
||||||
|
|
||||||
int min_blob_pixels;
|
int min_blob_pixels;
|
||||||
int max_blob_pixels;
|
int max_blob_pixels;
|
||||||
int min_blobs;
|
int min_blobs;
|
||||||
int max_blobs;
|
int max_blobs;
|
||||||
|
|
||||||
int overload_frames;
|
int overload_frames;
|
||||||
int extend_alarm_frames;
|
int extend_alarm_frames;
|
||||||
|
|
||||||
// Outputs/Statistics
|
// Outputs/Statistics
|
||||||
bool alarmed;
|
bool alarmed;
|
||||||
int pixel_diff;
|
int pixel_diff;
|
||||||
unsigned int alarm_pixels;
|
unsigned int alarm_pixels;
|
||||||
int alarm_filter_pixels;
|
int alarm_filter_pixels;
|
||||||
int alarm_blob_pixels;
|
int alarm_blob_pixels;
|
||||||
int alarm_blobs;
|
int alarm_blobs;
|
||||||
int min_blob_size;
|
int min_blob_size;
|
||||||
int max_blob_size;
|
int max_blob_size;
|
||||||
Box alarm_box;
|
Box alarm_box;
|
||||||
Coord alarm_centre;
|
Coord alarm_centre;
|
||||||
unsigned int score;
|
unsigned int score;
|
||||||
Image *pg_image;
|
Image *pg_image;
|
||||||
Range *ranges;
|
Range *ranges;
|
||||||
Image *image;
|
Image *image;
|
||||||
|
|
||||||
int overload_count;
|
int overload_count;
|
||||||
int extend_alarm_count;
|
int extend_alarm_count;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames );
|
||||||
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0)
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon )
|
||||||
{
|
{
|
||||||
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Zone();
|
~Zone();
|
||||||
|
|
||||||
inline int Id() const { return( id ); }
|
inline int Id() const { return( id ); }
|
||||||
inline const char *Label() const { return( label ); }
|
inline const char *Label() const { return( label ); }
|
||||||
inline ZoneType Type() const { return( type ); }
|
inline ZoneType Type() const { return( type ); }
|
||||||
inline bool IsActive() const { return( type == ACTIVE ); }
|
inline bool IsActive() const { return( type == ACTIVE ); }
|
||||||
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
inline bool IsInclusive() const { return( type == INCLUSIVE ); }
|
||||||
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
inline bool IsExclusive() const { return( type == EXCLUSIVE ); }
|
||||||
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
inline bool IsPreclusive() const { return( type == PRECLUSIVE ); }
|
||||||
inline bool IsInactive() const { return( type == INACTIVE ); }
|
inline bool IsInactive() const { return( type == INACTIVE ); }
|
||||||
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
inline bool IsPrivacy() const { return( type == PRIVACY ); }
|
||||||
inline const Image *AlarmImage() const { return( image ); }
|
inline const Image *AlarmImage() const { return( image ); }
|
||||||
inline const Polygon &GetPolygon() const { return( polygon ); }
|
inline const Polygon &GetPolygon() const { return( polygon ); }
|
||||||
inline bool Alarmed() const { return( alarmed ); }
|
inline bool Alarmed() const { return( alarmed ); }
|
||||||
inline void SetAlarm() { alarmed = true; }
|
inline void SetAlarm() { alarmed = true; }
|
||||||
inline void ClearAlarm() { alarmed = false; }
|
inline void ClearAlarm() { alarmed = false; }
|
||||||
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
inline Coord GetAlarmCentre() const { return( alarm_centre ); }
|
||||||
inline unsigned int Score() const { return( score ); }
|
inline unsigned int Score() const { return( score ); }
|
||||||
|
|
||||||
inline void ResetStats()
|
inline void ResetStats()
|
||||||
{
|
{
|
||||||
alarmed = false;
|
alarmed = false;
|
||||||
pixel_diff = 0;
|
pixel_diff = 0;
|
||||||
alarm_pixels = 0;
|
alarm_pixels = 0;
|
||||||
alarm_filter_pixels = 0;
|
alarm_filter_pixels = 0;
|
||||||
alarm_blob_pixels = 0;
|
alarm_blob_pixels = 0;
|
||||||
alarm_blobs = 0;
|
alarm_blobs = 0;
|
||||||
min_blob_size = 0;
|
min_blob_size = 0;
|
||||||
max_blob_size = 0;
|
max_blob_size = 0;
|
||||||
score = 0;
|
score = 0;
|
||||||
}
|
}
|
||||||
void RecordStats( const Event *event );
|
void RecordStats( const Event *event );
|
||||||
bool CheckAlarms( const Image *delta_image );
|
bool CheckAlarms( const Image *delta_image );
|
||||||
bool DumpSettings( char *output, bool verbose );
|
bool DumpSettings( char *output, bool verbose );
|
||||||
|
|
||||||
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
static bool ParsePolygonString( const char *polygon_string, Polygon &polygon );
|
||||||
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon );
|
||||||
static int Load( Monitor *monitor, Zone **&zones );
|
static int Load( Monitor *monitor, Zone **&zones );
|
||||||
//=================================================
|
//=================================================
|
||||||
bool CheckOverloadCount();
|
bool CheckOverloadCount();
|
||||||
int GetOverloadCount();
|
int GetOverloadCount();
|
||||||
void SetOverloadCount(int nOverCount);
|
void SetOverloadCount(int nOverCount);
|
||||||
int GetOverloadFrames();
|
int GetOverloadFrames();
|
||||||
//=================================================
|
//=================================================
|
||||||
bool CheckExtendAlarmCount();
|
bool CheckExtendAlarmCount();
|
||||||
int GetExtendAlarmCount();
|
int GetExtendAlarmCount();
|
||||||
void SetExtendAlarmCount(int nOverCount);
|
void SetExtendAlarmCount(int nOverCount);
|
||||||
int GetExtendAlarmFrames();
|
int GetExtendAlarmFrames();
|
||||||
void SetScore(unsigned int nScore);
|
void SetScore(unsigned int nScore);
|
||||||
void SetAlarmImage(const Image* srcImage);
|
void SetAlarmImage(const Image* srcImage);
|
||||||
|
|
||||||
inline const Image *getPgImage() const { return( pg_image ); }
|
inline const Image *getPgImage() const { return( pg_image ); }
|
||||||
inline const Range *getRanges() const { return( ranges ); }
|
inline const Range *getRanges() const { return( ranges ); }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
238
src/zma.cpp
238
src/zma.cpp
|
@ -41,9 +41,9 @@ behind.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-m, --monitor_id - ID of the monitor to analyse
|
-m, --monitor_id - ID of the monitor to analyse
|
||||||
-h, --help - Display usage information
|
-h, --help - Display usage information
|
||||||
-v, --version - Print the installed version of ZoneMinder
|
-v, --version - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
@ -59,146 +59,146 @@ behind.
|
||||||
|
|
||||||
void Usage()
|
void Usage()
|
||||||
{
|
{
|
||||||
fprintf( stderr, "zma -m <monitor_id>\n" );
|
fprintf( stderr, "zma -m <monitor_id>\n" );
|
||||||
fprintf( stderr, "Options:\n" );
|
fprintf( stderr, "Options:\n" );
|
||||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||||
fprintf( stderr, " -h, --help : This screen\n" );
|
fprintf( stderr, " -h, --help : This screen\n" );
|
||||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, char *argv[] )
|
int main( int argc, char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
int id = -1;
|
int id = -1;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"monitor", 1, 0, 'm'},
|
{"monitor", 1, 0, 'm'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, 'v'},
|
{"version", 0, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'm':
|
case 'm':
|
||||||
id = atoi(optarg);
|
id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
Usage();
|
Usage();
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
std::cout << ZM_VERSION << "\n";
|
std::cout << ZM_VERSION << "\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc)
|
if (optind < argc)
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Extraneous options, " );
|
fprintf( stderr, "Extraneous options, " );
|
||||||
while (optind < argc)
|
while (optind < argc)
|
||||||
printf ("%s ", argv[optind++]);
|
printf ("%s ", argv[optind++]);
|
||||||
printf ("\n");
|
printf ("\n");
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( id < 0 )
|
if ( id < 0 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_id_string[16];
|
char log_id_string[16];
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id );
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( log_id_string );
|
logInit( log_id_string );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
|
||||||
|
|
||||||
if ( monitor )
|
if ( monitor )
|
||||||
{
|
{
|
||||||
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
|
||||||
|
|
||||||
if ( config.opt_frame_server )
|
if ( config.opt_frame_server )
|
||||||
{
|
{
|
||||||
Event::OpenFrameSocket( monitor->Id() );
|
Event::OpenFrameSocket( monitor->Id() );
|
||||||
}
|
}
|
||||||
|
|
||||||
zmSetDefaultHupHandler();
|
zmSetDefaultHupHandler();
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
sigemptyset( &block_set );
|
sigemptyset( &block_set );
|
||||||
|
|
||||||
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
useconds_t analysis_rate = monitor->GetAnalysisRate();
|
||||||
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay();
|
||||||
time_t last_analysis_update_time, cur_time;
|
time_t last_analysis_update_time, cur_time;
|
||||||
monitor->UpdateAdaptiveSkip();
|
monitor->UpdateAdaptiveSkip();
|
||||||
last_analysis_update_time = time( 0 );
|
last_analysis_update_time = time( 0 );
|
||||||
|
|
||||||
while( !zm_terminate )
|
while( !zm_terminate )
|
||||||
{
|
{
|
||||||
// Process the next image
|
// Process the next image
|
||||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||||
|
|
||||||
// Some periodic updates are required for variable capturing framerate
|
// Some periodic updates are required for variable capturing framerate
|
||||||
if ( analysis_update_delay )
|
if ( analysis_update_delay )
|
||||||
{
|
{
|
||||||
cur_time = time( 0 );
|
cur_time = time( 0 );
|
||||||
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay )
|
||||||
{
|
{
|
||||||
analysis_rate = monitor->GetAnalysisRate();
|
analysis_rate = monitor->GetAnalysisRate();
|
||||||
monitor->UpdateAdaptiveSkip();
|
monitor->UpdateAdaptiveSkip();
|
||||||
last_analysis_update_time = cur_time;
|
last_analysis_update_time = cur_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !monitor->Analyse() )
|
if ( !monitor->Analyse() )
|
||||||
{
|
{
|
||||||
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
|
||||||
}
|
}
|
||||||
else if ( analysis_rate )
|
else if ( analysis_rate )
|
||||||
{
|
{
|
||||||
usleep( analysis_rate );
|
usleep( analysis_rate );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( zm_reload )
|
if ( zm_reload )
|
||||||
{
|
{
|
||||||
monitor->Reload();
|
monitor->Reload();
|
||||||
zm_reload = false;
|
zm_reload = false;
|
||||||
}
|
}
|
||||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||||
}
|
}
|
||||||
delete monitor;
|
delete monitor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||||
}
|
}
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
508
src/zmc.cpp
508
src/zmc.cpp
|
@ -44,12 +44,12 @@ possible, this should run at more or less constant speed.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
|
||||||
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
-r <proto> -H <host> -P <port> -p <path> - For remote cameras
|
||||||
-f, --file <file_path> - For local images, jpg file to access.
|
-f, --file <file_path> - For local images, jpg file to access.
|
||||||
-m, --monitor_id - ID of the monitor to analyse
|
-m, --monitor_id - ID of the monitor to analyse
|
||||||
-h, --help - Display usage information
|
-h, --help - Display usage information
|
||||||
-v, --version - Print the installed version of ZoneMinder
|
-v, --version - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
@ -75,290 +75,290 @@ possible, this should run at more or less constant speed.
|
||||||
|
|
||||||
void Usage()
|
void Usage()
|
||||||
{
|
{
|
||||||
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
fprintf( stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n" );
|
||||||
|
|
||||||
fprintf( stderr, "Options:\n" );
|
fprintf( stderr, "Options:\n" );
|
||||||
#if defined(BSD)
|
#if defined(BSD)
|
||||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n" );
|
||||||
#else
|
#else
|
||||||
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
fprintf( stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n" );
|
||||||
#endif
|
#endif
|
||||||
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
fprintf( stderr, " -r <proto> -H <host> -P <port> -p <path> : For remote cameras\n" );
|
||||||
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
fprintf( stderr, " -f, --file <file_path> : For local images, jpg file to access.\n" );
|
||||||
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
fprintf( stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n" );
|
||||||
fprintf( stderr, " -h, --help : This screen\n" );
|
fprintf( stderr, " -h, --help : This screen\n" );
|
||||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, char *argv[] )
|
int main( int argc, char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
const char *device = "";
|
const char *device = "";
|
||||||
const char *protocol = "";
|
const char *protocol = "";
|
||||||
const char *host = "";
|
const char *host = "";
|
||||||
const char *port = "";
|
const char *port = "";
|
||||||
const char *path = "";
|
const char *path = "";
|
||||||
const char *file = "";
|
const char *file = "";
|
||||||
int monitor_id = -1;
|
int monitor_id = -1;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"device", 1, 0, 'd'},
|
{"device", 1, 0, 'd'},
|
||||||
{"protocol", 1, 0, 'r'},
|
{"protocol", 1, 0, 'r'},
|
||||||
{"host", 1, 0, 'H'},
|
{"host", 1, 0, 'H'},
|
||||||
{"port", 1, 0, 'P'},
|
{"port", 1, 0, 'P'},
|
||||||
{"path", 1, 0, 'p'},
|
{"path", 1, 0, 'p'},
|
||||||
{"file", 1, 0, 'f'},
|
{"file", 1, 0, 'f'},
|
||||||
{"monitor", 1, 0, 'm'},
|
{"monitor", 1, 0, 'm'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, 'v'},
|
{"version", 0, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'd':
|
case 'd':
|
||||||
device = optarg;
|
device = optarg;
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
host = optarg;
|
host = optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
port = optarg;
|
port = optarg;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
path = optarg;
|
path = optarg;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
file = optarg;
|
file = optarg;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
monitor_id = atoi(optarg);
|
monitor_id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
Usage();
|
Usage();
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
std::cout << ZM_VERSION << "\n";
|
std::cout << ZM_VERSION << "\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc)
|
if (optind < argc)
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Extraneous options, " );
|
fprintf( stderr, "Extraneous options, " );
|
||||||
while (optind < argc)
|
while (optind < argc)
|
||||||
printf ("%s ", argv[optind++]);
|
printf ("%s ", argv[optind++]);
|
||||||
printf ("\n");
|
printf ("\n");
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) );
|
||||||
if ( modes > 1 )
|
if ( modes > 1 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( modes < 1 )
|
if ( modes < 1 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_id_string[32] = "";
|
char log_id_string[32] = "";
|
||||||
if ( device[0] )
|
if ( device[0] )
|
||||||
{
|
{
|
||||||
const char *slash_ptr = strrchr( device, '/' );
|
const char *slash_ptr = strrchr( device, '/' );
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device );
|
||||||
}
|
}
|
||||||
else if ( host[0] )
|
else if ( host[0] )
|
||||||
{
|
{
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host );
|
||||||
}
|
}
|
||||||
else if ( file[0] )
|
else if ( file[0] )
|
||||||
{
|
{
|
||||||
const char *slash_ptr = strrchr( file, '/' );
|
const char *slash_ptr = strrchr( file, '/' );
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( log_id_string );
|
logInit( log_id_string );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
Monitor **monitors = 0;
|
Monitor **monitors = 0;
|
||||||
int n_monitors = 0;
|
int n_monitors = 0;
|
||||||
#if ZM_HAS_V4L
|
#if ZM_HAS_V4L
|
||||||
if ( device[0] )
|
if ( device[0] )
|
||||||
{
|
{
|
||||||
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
if ( host[0] )
|
if ( host[0] )
|
||||||
{
|
{
|
||||||
if ( !port )
|
if ( !port )
|
||||||
port = "80";
|
port = "80";
|
||||||
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else if ( file[0] )
|
else if ( file[0] )
|
||||||
{
|
{
|
||||||
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE );
|
||||||
if ( monitor )
|
if ( monitor )
|
||||||
{
|
{
|
||||||
monitors = new Monitor *[1];
|
monitors = new Monitor *[1];
|
||||||
monitors[0] = monitor;
|
monitors[0] = monitor;
|
||||||
n_monitors = 1;
|
n_monitors = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !n_monitors )
|
if ( !n_monitors )
|
||||||
{
|
{
|
||||||
Error( "No monitors found" );
|
Error( "No monitors found" );
|
||||||
exit ( -1 );
|
exit ( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Info( "Starting Capture version %s", ZM_VERSION );
|
Info( "Starting Capture version %s", ZM_VERSION );
|
||||||
|
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
sigemptyset( &block_set );
|
sigemptyset( &block_set );
|
||||||
|
|
||||||
sigaddset( &block_set, SIGUSR1 );
|
sigaddset( &block_set, SIGUSR1 );
|
||||||
sigaddset( &block_set, SIGUSR2 );
|
sigaddset( &block_set, SIGUSR2 );
|
||||||
|
|
||||||
if ( monitors[0]->PrimeCapture() < 0 )
|
if ( monitors[0]->PrimeCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to prime capture of initial monitor" );
|
Error( "Failed to prime capture of initial monitor" );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
long *capture_delays = new long[n_monitors];
|
long *capture_delays = new long[n_monitors];
|
||||||
long *alarm_capture_delays = new long[n_monitors];
|
long *alarm_capture_delays = new long[n_monitors];
|
||||||
long *next_delays = new long[n_monitors];
|
long *next_delays = new long[n_monitors];
|
||||||
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
struct timeval * last_capture_times = new struct timeval[n_monitors];
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
struct DeltaTimeval delta_time;
|
struct DeltaTimeval delta_time;
|
||||||
while( !zm_terminate )
|
while( !zm_terminate )
|
||||||
{
|
{
|
||||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
long min_delay = MAXINT;
|
long min_delay = MAXINT;
|
||||||
|
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
for ( int j = 0; j < n_monitors; j++ )
|
for ( int j = 0; j < n_monitors; j++ )
|
||||||
{
|
{
|
||||||
if ( last_capture_times[j].tv_sec )
|
if ( last_capture_times[j].tv_sec )
|
||||||
{
|
{
|
||||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 );
|
||||||
if ( monitors[i]->GetState() == Monitor::ALARM )
|
if ( monitors[i]->GetState() == Monitor::ALARM )
|
||||||
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
|
||||||
else
|
else
|
||||||
next_delays[j] = capture_delays[j]-delta_time.delta;
|
next_delays[j] = capture_delays[j]-delta_time.delta;
|
||||||
if ( next_delays[j] < 0 )
|
if ( next_delays[j] < 0 )
|
||||||
next_delays[j] = 0;
|
next_delays[j] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
next_delays[j] = 0;
|
next_delays[j] = 0;
|
||||||
}
|
}
|
||||||
if ( next_delays[j] <= min_delay )
|
if ( next_delays[j] <= min_delay )
|
||||||
{
|
{
|
||||||
min_delay = next_delays[j];
|
min_delay = next_delays[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 )
|
||||||
{
|
{
|
||||||
if ( monitors[i]->PreCapture() < 0 )
|
if ( monitors[i]->PreCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( monitors[i]->Capture() < 0 )
|
if ( monitors[i]->Capture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( monitors[i]->PostCapture() < 0 )
|
if ( monitors[i]->PostCapture() < 0 )
|
||||||
{
|
{
|
||||||
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors );
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
result = -1;
|
result = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( next_delays[i] > 0 )
|
if ( next_delays[i] > 0 )
|
||||||
{
|
{
|
||||||
gettimeofday( &now, NULL );
|
gettimeofday( &now, NULL );
|
||||||
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 );
|
||||||
long sleep_time = next_delays[i]-delta_time.delta;
|
long sleep_time = next_delays[i]-delta_time.delta;
|
||||||
if ( sleep_time > 0 )
|
if ( sleep_time > 0 )
|
||||||
{
|
{
|
||||||
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gettimeofday( &(last_capture_times[i]), NULL );
|
gettimeofday( &(last_capture_times[i]), NULL );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < n_monitors; i++ )
|
for ( int i = 0; i < n_monitors; i++ )
|
||||||
{
|
{
|
||||||
delete monitors[i];
|
delete monitors[i];
|
||||||
}
|
}
|
||||||
delete [] monitors;
|
delete [] monitors;
|
||||||
delete [] alarm_capture_delays;
|
delete [] alarm_capture_delays;
|
||||||
delete [] capture_delays;
|
delete [] capture_delays;
|
||||||
delete [] next_delays;
|
delete [] next_delays;
|
||||||
delete [] last_capture_times;
|
delete [] last_capture_times;
|
||||||
|
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
|
|
||||||
return( result );
|
return( result );
|
||||||
}
|
}
|
||||||
|
|
448
src/zmf.cpp
448
src/zmf.cpp
|
@ -42,9 +42,9 @@ them itself.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-m, --monitor_id - ID of the monitor to use
|
-m, --monitor_id - ID of the monitor to use
|
||||||
-h, --help - Display usage information
|
-h, --help - Display usage information
|
||||||
-v, --version - Print the installed version of ZoneMinder
|
-v, --version - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
@ -73,278 +73,278 @@ them itself.
|
||||||
|
|
||||||
int OpenSocket( int monitor_id )
|
int OpenSocket( int monitor_id )
|
||||||
{
|
{
|
||||||
int sd = socket( AF_UNIX, SOCK_STREAM, 0);
|
int sd = socket( AF_UNIX, SOCK_STREAM, 0);
|
||||||
if ( sd < 0 )
|
if ( sd < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't create socket: %s", strerror(errno) );
|
Error( "Can't create socket: %s", strerror(errno) );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char sock_path[PATH_MAX] = "";
|
char sock_path[PATH_MAX] = "";
|
||||||
snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id );
|
snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id );
|
||||||
if ( unlink( sock_path ) < 0 )
|
if ( unlink( sock_path ) < 0 )
|
||||||
{
|
{
|
||||||
Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) );
|
Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
|
|
||||||
strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) );
|
strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) );
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 )
|
if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't bind: %s", strerror(errno) );
|
Error( "Can't bind: %s", strerror(errno) );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( listen( sd, SOMAXCONN ) < 0 )
|
if ( listen( sd, SOMAXCONN ) < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't listen: %s", strerror(errno) );
|
Error( "Can't listen: %s", strerror(errno) );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_un rem_addr;
|
struct sockaddr_un rem_addr;
|
||||||
socklen_t rem_addr_len = sizeof(rem_addr);
|
socklen_t rem_addr_len = sizeof(rem_addr);
|
||||||
int new_sd = -1;
|
int new_sd = -1;
|
||||||
if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 )
|
if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't accept: %s", strerror(errno) );
|
Error( "Can't accept: %s", strerror(errno) );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
close( sd );
|
close( sd );
|
||||||
|
|
||||||
sd = new_sd;
|
sd = new_sd;
|
||||||
|
|
||||||
Info( "Frame server socket open, awaiting images" );
|
Info( "Frame server socket open, awaiting images" );
|
||||||
return( sd );
|
return( sd );
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReopenSocket( int &sd, int monitor_id )
|
int ReopenSocket( int &sd, int monitor_id )
|
||||||
{
|
{
|
||||||
close( sd );
|
close( sd );
|
||||||
return( sd = OpenSocket( monitor_id ) );
|
return( sd = OpenSocket( monitor_id ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Usage()
|
void Usage()
|
||||||
{
|
{
|
||||||
fprintf( stderr, "zmf -m <monitor_id>\n" );
|
fprintf( stderr, "zmf -m <monitor_id>\n" );
|
||||||
fprintf( stderr, "Options:\n" );
|
fprintf( stderr, "Options:\n" );
|
||||||
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
|
||||||
fprintf( stderr, " -h, --help : This screen\n" );
|
fprintf( stderr, " -h, --help : This screen\n" );
|
||||||
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" );
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, char *argv[] )
|
int main( int argc, char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
int id = -1;
|
int id = -1;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"monitor", 1, 0, 'm'},
|
{"monitor", 1, 0, 'm'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, 'v'},
|
{"version", 0, 0, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
|
||||||
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'm':
|
case 'm':
|
||||||
id = atoi(optarg);
|
id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
Usage();
|
Usage();
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
std::cout << ZM_VERSION << "\n";
|
std::cout << ZM_VERSION << "\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
default:
|
default:
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc)
|
if (optind < argc)
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Extraneous options, " );
|
fprintf( stderr, "Extraneous options, " );
|
||||||
while (optind < argc)
|
while (optind < argc)
|
||||||
printf ("%s ", argv[optind++]);
|
printf ("%s ", argv[optind++]);
|
||||||
printf ("\n");
|
printf ("\n");
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( id < 0 )
|
if ( id < 0 )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Bogus monitor %d\n", id );
|
fprintf( stderr, "Bogus monitor %d\n", id );
|
||||||
Usage();
|
Usage();
|
||||||
exit( 0 );
|
exit( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_id_string[16];
|
char log_id_string[16];
|
||||||
snprintf( log_id_string, sizeof(log_id_string), "m%d", id );
|
snprintf( log_id_string, sizeof(log_id_string), "m%d", id );
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( "zmf" );
|
logInit( "zmf" );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY );
|
Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY );
|
||||||
|
|
||||||
if ( !monitor )
|
if ( !monitor )
|
||||||
{
|
{
|
||||||
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
fprintf( stderr, "Can't find monitor with id of %d\n", id );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
char capt_path[PATH_MAX];
|
char capt_path[PATH_MAX];
|
||||||
char anal_path[PATH_MAX];
|
char anal_path[PATH_MAX];
|
||||||
snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||||
snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits );
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
sigset_t block_set;
|
sigset_t block_set;
|
||||||
sigemptyset( &block_set );
|
sigemptyset( &block_set );
|
||||||
|
|
||||||
int sd = OpenSocket( monitor->Id() );
|
int sd = OpenSocket( monitor->Id() );
|
||||||
|
|
||||||
FrameHeader frame_header = { 0, 0, false, 0 };
|
FrameHeader frame_header = { 0, 0, false, 0 };
|
||||||
//unsigned char *image_data = 0;
|
//unsigned char *image_data = 0;
|
||||||
|
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
|
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
timeout.tv_sec = 1;
|
timeout.tv_sec = 1;
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
while( 1 )
|
while( 1 )
|
||||||
{
|
{
|
||||||
struct timeval temp_timeout = timeout;
|
struct timeval temp_timeout = timeout;
|
||||||
|
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(sd, &rfds);
|
FD_SET(sd, &rfds);
|
||||||
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
|
int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout );
|
||||||
if( n_found == 0 )
|
if( n_found == 0 )
|
||||||
{
|
{
|
||||||
Debug( 1, "Select timed out" );
|
Debug( 1, "Select timed out" );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if ( n_found < 0)
|
else if ( n_found < 0)
|
||||||
{
|
{
|
||||||
Error( "Select error: %s", strerror(errno) );
|
Error( "Select error: %s", strerror(errno) );
|
||||||
ReopenSocket( sd, monitor->Id() );
|
ReopenSocket( sd, monitor->Id() );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
sigprocmask( SIG_BLOCK, &block_set, 0 );
|
||||||
|
|
||||||
int n_bytes = read( sd, &frame_header, sizeof(frame_header) );
|
int n_bytes = read( sd, &frame_header, sizeof(frame_header) );
|
||||||
if ( n_bytes != sizeof(frame_header) )
|
if ( n_bytes != sizeof(frame_header) )
|
||||||
{
|
{
|
||||||
if ( n_bytes < 0 )
|
if ( n_bytes < 0 )
|
||||||
{
|
{
|
||||||
Error( "Can't read frame header: %s", strerror(errno) );
|
Error( "Can't read frame header: %s", strerror(errno) );
|
||||||
}
|
}
|
||||||
else if ( n_bytes > 0 )
|
else if ( n_bytes > 0 )
|
||||||
{
|
{
|
||||||
Error( "Incomplete read of frame header, %d bytes only", n_bytes );
|
Error( "Incomplete read of frame header, %d bytes only", n_bytes );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Warning( "Socket closed at remote end" );
|
Warning( "Socket closed at remote end" );
|
||||||
}
|
}
|
||||||
ReopenSocket( sd, monitor->Id() );
|
ReopenSocket( sd, monitor->Id() );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length );
|
Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length );
|
||||||
static unsigned char image_data[ZM_MAX_IMAGE_SIZE];
|
static unsigned char image_data[ZM_MAX_IMAGE_SIZE];
|
||||||
|
|
||||||
// Read for pipe and loop until bytes expected have been read or an error occurs
|
// Read for pipe and loop until bytes expected have been read or an error occurs
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read );
|
n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read );
|
||||||
if (n_bytes < 0) break; // break on error
|
if (n_bytes < 0) break; // break on error
|
||||||
if (n_bytes < (int)frame_header.image_length)
|
if (n_bytes < (int)frame_header.image_length)
|
||||||
{
|
{
|
||||||
// print some informational messages
|
// print some informational messages
|
||||||
if (bytes_read == 0)
|
if (bytes_read == 0)
|
||||||
{
|
|
||||||
Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
|
|
||||||
}
|
|
||||||
else if (bytes_read+n_bytes == (int)frame_header.image_length)
|
|
||||||
{
|
|
||||||
Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bytes_read+= n_bytes;
|
|
||||||
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
|
|
||||||
|
|
||||||
// Print errors if there was a problem
|
|
||||||
if ( n_bytes < 1 )
|
|
||||||
{
|
{
|
||||||
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length);
|
Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length);
|
||||||
if ( n_bytes < 0 )
|
}
|
||||||
{
|
else if (bytes_read+n_bytes == (int)frame_header.image_length)
|
||||||
Error( "Can't read frame image data: %s", strerror(errno) );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Warning( "Socket closed at remote end" );
|
|
||||||
}
|
|
||||||
ReopenSocket( sd, monitor->Id() );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char subpath[PATH_MAX] = "";
|
|
||||||
if ( config.use_deep_storage )
|
|
||||||
{
|
{
|
||||||
struct tm *time = localtime( &frame_header.event_time );
|
Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length);
|
||||||
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id );
|
Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
bytes_read+= n_bytes;
|
||||||
|
} while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) );
|
||||||
|
|
||||||
static char path[PATH_MAX] = "";
|
// Print errors if there was a problem
|
||||||
snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id );
|
if ( n_bytes < 1 )
|
||||||
Debug( 1, "Got image, writing to %s", path );
|
{
|
||||||
|
Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length);
|
||||||
|
if ( n_bytes < 0 )
|
||||||
|
{
|
||||||
|
Error( "Can't read frame image data: %s", strerror(errno) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning( "Socket closed at remote end" );
|
||||||
|
}
|
||||||
|
ReopenSocket( sd, monitor->Id() );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *fd = 0;
|
static char subpath[PATH_MAX] = "";
|
||||||
if ( (fd = fopen( path, "w" )) < 0 )
|
if ( config.use_deep_storage )
|
||||||
{
|
{
|
||||||
Error( "Can't fopen '%s': %s", path, strerror(errno) );
|
struct tm *time = localtime( &frame_header.event_time );
|
||||||
exit( -1 );
|
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
|
||||||
}
|
}
|
||||||
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) )
|
else
|
||||||
{
|
{
|
||||||
Error( "Can't fwrite image data: %s", strerror(errno) );
|
snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id );
|
||||||
exit( -1 );
|
}
|
||||||
}
|
|
||||||
fclose( fd );
|
|
||||||
|
|
||||||
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
static char path[PATH_MAX] = "";
|
||||||
}
|
snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id );
|
||||||
logTerm();
|
Debug( 1, "Got image, writing to %s", path );
|
||||||
zmDbClose();
|
|
||||||
|
FILE *fd = 0;
|
||||||
|
if ( (fd = fopen( path, "w" )) < 0 )
|
||||||
|
{
|
||||||
|
Error( "Can't fopen '%s': %s", path, strerror(errno) );
|
||||||
|
exit( -1 );
|
||||||
|
}
|
||||||
|
if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) )
|
||||||
|
{
|
||||||
|
Error( "Can't fwrite image data: %s", strerror(errno) );
|
||||||
|
exit( -1 );
|
||||||
|
}
|
||||||
|
fclose( fd );
|
||||||
|
|
||||||
|
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
|
||||||
|
}
|
||||||
|
logTerm();
|
||||||
|
zmDbClose();
|
||||||
}
|
}
|
||||||
|
|
10
src/zmf.h
10
src/zmf.h
|
@ -22,11 +22,11 @@
|
||||||
|
|
||||||
struct FrameHeader
|
struct FrameHeader
|
||||||
{
|
{
|
||||||
unsigned long event_id;
|
unsigned long event_id;
|
||||||
time_t event_time;
|
time_t event_time;
|
||||||
unsigned long frame_id;
|
unsigned long frame_id;
|
||||||
bool alarm_frame;
|
bool alarm_frame;
|
||||||
unsigned long image_length;
|
unsigned long image_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZMFILE_H
|
#endif // ZMFILE_H
|
||||||
|
|
592
src/zms.cpp
592
src/zms.cpp
|
@ -28,318 +28,318 @@
|
||||||
|
|
||||||
bool ValidateAccess( User *user, int mon_id )
|
bool ValidateAccess( User *user, int mon_id )
|
||||||
{
|
{
|
||||||
bool allowed = true;
|
bool allowed = true;
|
||||||
|
|
||||||
if ( mon_id > 0 )
|
if ( mon_id > 0 )
|
||||||
{
|
{
|
||||||
if ( user->getStream() < User::PERM_VIEW )
|
if ( user->getStream() < User::PERM_VIEW )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
if ( !user->canAccess( mon_id ) )
|
if ( !user->canAccess( mon_id ) )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ( user->getEvents() < User::PERM_VIEW )
|
if ( user->getEvents() < User::PERM_VIEW )
|
||||||
allowed = false;
|
allowed = false;
|
||||||
}
|
}
|
||||||
if ( !allowed )
|
if ( !allowed )
|
||||||
{
|
{
|
||||||
Error( "Error, insufficient privileges for requested action" );
|
Error( "Error, insufficient privileges for requested action" );
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
return( allowed );
|
return( allowed );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main( int argc, const char *argv[] )
|
int main( int argc, const char *argv[] )
|
||||||
{
|
{
|
||||||
self = argv[0];
|
self = argv[0];
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
|
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
|
||||||
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
||||||
char format[32] = "";
|
char format[32] = "";
|
||||||
int monitor_id = 0;
|
int monitor_id = 0;
|
||||||
time_t event_time = 0;
|
time_t event_time = 0;
|
||||||
int event_id = 0;
|
int event_id = 0;
|
||||||
unsigned int frame_id = 1;
|
unsigned int frame_id = 1;
|
||||||
unsigned int scale = 100;
|
unsigned int scale = 100;
|
||||||
unsigned int rate = 100;
|
unsigned int rate = 100;
|
||||||
double maxfps = 10.0;
|
double maxfps = 10.0;
|
||||||
unsigned int bitrate = 100000;
|
unsigned int bitrate = 100000;
|
||||||
unsigned int ttl = 0;
|
unsigned int ttl = 0;
|
||||||
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
|
EventStream::StreamMode replay = EventStream::MODE_SINGLE;
|
||||||
char username[64] = "";
|
char username[64] = "";
|
||||||
char password[64] = "";
|
char password[64] = "";
|
||||||
char auth[64] = "";
|
char auth[64] = "";
|
||||||
unsigned int connkey = 0;
|
unsigned int connkey = 0;
|
||||||
unsigned int playback_buffer = 0;
|
unsigned int playback_buffer = 0;
|
||||||
|
|
||||||
bool nph = false;
|
bool nph = false;
|
||||||
const char *basename = strrchr( argv[0], '/' );
|
const char *basename = strrchr( argv[0], '/' );
|
||||||
if (basename) //if we found a / lets skip past it
|
if (basename) //if we found a / lets skip past it
|
||||||
basename++;
|
basename++;
|
||||||
else //argv[0] will not always contain the full path, but rather just the script name
|
else //argv[0] will not always contain the full path, but rather just the script name
|
||||||
basename = argv[0];
|
basename = argv[0];
|
||||||
const char *nph_prefix = "nph-";
|
const char *nph_prefix = "nph-";
|
||||||
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
|
if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) )
|
||||||
{
|
{
|
||||||
nph = true;
|
nph = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
|
||||||
logInit( "zms" );
|
logInit( "zms" );
|
||||||
|
|
||||||
ssedetect();
|
ssedetect();
|
||||||
|
|
||||||
zmSetDefaultTermHandler();
|
zmSetDefaultTermHandler();
|
||||||
zmSetDefaultDieHandler();
|
zmSetDefaultDieHandler();
|
||||||
|
|
||||||
const char *query = getenv( "QUERY_STRING" );
|
const char *query = getenv( "QUERY_STRING" );
|
||||||
if ( query )
|
if ( query )
|
||||||
{
|
{
|
||||||
Debug( 1, "Query: %s", query );
|
Debug( 1, "Query: %s", query );
|
||||||
|
|
||||||
char temp_query[1024];
|
char temp_query[1024];
|
||||||
strncpy( temp_query, query, sizeof(temp_query) );
|
strncpy( temp_query, query, sizeof(temp_query) );
|
||||||
char *q_ptr = temp_query;
|
char *q_ptr = temp_query;
|
||||||
char *parms[16]; // Shouldn't be more than this
|
char *parms[16]; // Shouldn't be more than this
|
||||||
int parm_no = 0;
|
int parm_no = 0;
|
||||||
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
|
while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) )
|
||||||
{
|
|
||||||
parm_no++;
|
|
||||||
q_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int p = 0; p < parm_no; p++ )
|
|
||||||
{
|
|
||||||
char *name = strtok( parms[p], "=" );
|
|
||||||
char *value = strtok( NULL, "=" );
|
|
||||||
if ( !value )
|
|
||||||
value = (char *)"";
|
|
||||||
if ( !strcmp( name, "source" ) )
|
|
||||||
{
|
|
||||||
source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR;
|
|
||||||
}
|
|
||||||
else if ( !strcmp( name, "mode" ) )
|
|
||||||
{
|
|
||||||
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
|
||||||
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
|
||||||
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
|
||||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
|
||||||
}
|
|
||||||
else if ( !strcmp( name, "format" ) )
|
|
||||||
strncpy( format, value, sizeof(format) );
|
|
||||||
else if ( !strcmp( name, "monitor" ) )
|
|
||||||
monitor_id = atoi( value );
|
|
||||||
else if ( !strcmp( name, "time" ) )
|
|
||||||
event_time = atoi( value );
|
|
||||||
else if ( !strcmp( name, "event" ) )
|
|
||||||
event_id = strtoull( value, (char **)NULL, 10 );
|
|
||||||
else if ( !strcmp( name, "frame" ) )
|
|
||||||
frame_id = strtoull( value, (char **)NULL, 10 );
|
|
||||||
else if ( !strcmp( name, "scale" ) )
|
|
||||||
scale = atoi( value );
|
|
||||||
else if ( !strcmp( name, "rate" ) )
|
|
||||||
rate = atoi( value );
|
|
||||||
else if ( !strcmp( name, "maxfps" ) )
|
|
||||||
maxfps = atof( value );
|
|
||||||
else if ( !strcmp( name, "bitrate" ) )
|
|
||||||
bitrate = atoi( value );
|
|
||||||
else if ( !strcmp( name, "ttl" ) )
|
|
||||||
ttl = atoi(value);
|
|
||||||
else if ( !strcmp( name, "replay" ) )
|
|
||||||
{
|
|
||||||
replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE;
|
|
||||||
replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay;
|
|
||||||
}
|
|
||||||
else if ( !strcmp( name, "connkey" ) )
|
|
||||||
connkey = atoi(value);
|
|
||||||
else if ( !strcmp( name, "buffer" ) )
|
|
||||||
playback_buffer = atoi(value);
|
|
||||||
else if ( config.opt_use_auth )
|
|
||||||
{
|
|
||||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( !strcmp( name, "user" ) )
|
|
||||||
{
|
|
||||||
strncpy( username, value, sizeof(username) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( !strcmp( name, "auth" ) )
|
|
||||||
{
|
|
||||||
strncpy( auth, value, sizeof(auth) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( !strcmp( name, "user" ) )
|
|
||||||
{
|
|
||||||
strncpy( username, value, sizeof(username) );
|
|
||||||
}
|
|
||||||
if ( !strcmp( name, "pass" ) )
|
|
||||||
{
|
|
||||||
strncpy( password, value, sizeof(password) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( config.opt_use_auth )
|
|
||||||
{
|
|
||||||
User *user = 0;
|
|
||||||
|
|
||||||
if ( strcmp( config.auth_relay, "none" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( *username )
|
|
||||||
{
|
|
||||||
user = zmLoadUser( username );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( *auth )
|
|
||||||
{
|
|
||||||
user = zmLoadAuthUser( auth, config.auth_hash_ips );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( *username && *password )
|
|
||||||
{
|
|
||||||
user = zmLoadUser( username, password );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !user )
|
|
||||||
{
|
|
||||||
Error( "Unable to authenticate user" );
|
|
||||||
logTerm();
|
|
||||||
zmDbClose();
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
ValidateAccess( user, monitor_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
setbuf( stdout, 0 );
|
|
||||||
if ( nph )
|
|
||||||
{
|
|
||||||
fprintf( stdout, "HTTP/1.0 200 OK\r\n" );
|
|
||||||
}
|
|
||||||
fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION );
|
|
||||||
|
|
||||||
time_t now = time( 0 );
|
|
||||||
char date_string[64];
|
|
||||||
strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) );
|
|
||||||
|
|
||||||
fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" );
|
|
||||||
fprintf( stdout, "Last-Modified: %s\r\n", date_string );
|
|
||||||
fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" );
|
|
||||||
fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" );
|
|
||||||
fprintf( stdout, "Pragma: no-cache\r\n");
|
|
||||||
// Removed as causing more problems than it fixed.
|
|
||||||
//if ( !nph )
|
|
||||||
//{
|
|
||||||
//fprintf( stdout, "Content-Length: 0\r\n");
|
|
||||||
//}
|
|
||||||
|
|
||||||
if ( source == ZMS_MONITOR )
|
|
||||||
{
|
|
||||||
MonitorStream stream;
|
|
||||||
stream.setStreamScale( scale );
|
|
||||||
stream.setStreamReplayRate( rate );
|
|
||||||
stream.setStreamMaxFPS( maxfps );
|
|
||||||
stream.setStreamTTL( ttl );
|
|
||||||
stream.setStreamQueue( connkey );
|
|
||||||
stream.setStreamBuffer( playback_buffer );
|
|
||||||
if ( ! stream.setStreamStart( monitor_id ) ) {
|
|
||||||
Error( "Unable to connect to zmc process for monitor %d", monitor_id );
|
|
||||||
fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." );
|
|
||||||
logTerm();
|
|
||||||
zmDbClose();
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( mode == ZMS_JPEG )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_JPEG );
|
|
||||||
}
|
|
||||||
else if ( mode == ZMS_RAW )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_RAW );
|
|
||||||
}
|
|
||||||
else if ( mode == ZMS_ZIP )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_ZIP );
|
|
||||||
}
|
|
||||||
else if ( mode == ZMS_SINGLE )
|
|
||||||
{
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_SINGLE );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if HAVE_LIBAVCODEC
|
|
||||||
stream.setStreamFormat( format );
|
|
||||||
stream.setStreamBitrate( bitrate );
|
|
||||||
stream.setStreamType( MonitorStream::STREAM_MPEG );
|
|
||||||
#else // HAVE_LIBAVCODEC
|
|
||||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
|
||||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" );
|
|
||||||
logTerm();
|
|
||||||
zmDbClose();
|
|
||||||
return( -1 );
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
|
||||||
}
|
|
||||||
stream.runStream();
|
|
||||||
}
|
|
||||||
else if ( source == ZMS_EVENT )
|
|
||||||
{
|
{
|
||||||
EventStream stream;
|
parm_no++;
|
||||||
stream.setStreamScale( scale );
|
q_ptr = NULL;
|
||||||
stream.setStreamReplayRate( rate );
|
|
||||||
stream.setStreamMaxFPS( maxfps );
|
|
||||||
stream.setStreamMode( replay );
|
|
||||||
stream.setStreamQueue( connkey );
|
|
||||||
if ( monitor_id && event_time )
|
|
||||||
{
|
|
||||||
stream.setStreamStart( monitor_id, event_time );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream.setStreamStart( event_id, frame_id );
|
|
||||||
}
|
|
||||||
if ( mode == ZMS_JPEG )
|
|
||||||
{
|
|
||||||
stream.setStreamType( EventStream::STREAM_JPEG );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if HAVE_LIBAVCODEC
|
|
||||||
stream.setStreamFormat( format );
|
|
||||||
stream.setStreamBitrate( bitrate );
|
|
||||||
stream.setStreamType( EventStream::STREAM_MPEG );
|
|
||||||
#else // HAVE_LIBAVCODEC
|
|
||||||
Error( "MPEG streaming of '%s' attempted while disabled", query );
|
|
||||||
fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" );
|
|
||||||
logTerm();
|
|
||||||
zmDbClose();
|
|
||||||
return( -1 );
|
|
||||||
#endif // HAVE_LIBAVCODEC
|
|
||||||
}
|
|
||||||
stream.runStream();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logTerm();
|
for ( int p = 0; p < parm_no; p++ )
|
||||||
zmDbClose();
|
{
|
||||||
|
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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Streamer, $Date: 2010-10-14 23:21:00 +0200 (Thu, 14 Oct 2010) $
|
||||||
|
// Copyright (C) 2001-2010 Philip Coombes, Chris Kistner
|
||||||
|
//
|
||||||
|
// This program is based on revision 3143 of
|
||||||
|
// http://svn.zoneminder.com/svn/zm/trunk/src/zms.cpp
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
zmstreamer - eyeZM video streamer
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
zmstreamer -e <mode>
|
||||||
|
zmstreamer -o <format>
|
||||||
|
zmstreamer -u <buffer size>
|
||||||
|
zmstreamer -f <maximum fps>
|
||||||
|
zmstreamer -s <scale>
|
||||||
|
zmstreamer -b <bitrate in bps>
|
||||||
|
zmstreamer -m <monitor id>
|
||||||
|
zmstreamer -d <debug mode>
|
||||||
|
zmstreamer -i
|
||||||
|
zmstreamer -?
|
||||||
|
zmstreamer -h
|
||||||
|
zmstreamer -v
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
*DEPRECIATED* The xml skin and all files associated with the xml skin are now
|
||||||
|
depreciated. Please use the ZoneMinder API instead.
|
||||||
|
|
||||||
|
This binary works in conjunction with the XML skin to stream video to iPhones
|
||||||
|
running the eyeZm app.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
-e <mode> - Specify output mode: mpeg/jpg/zip/single/raw.
|
||||||
|
-o <format> - Specify output format.
|
||||||
|
-u <buffer size> - Specify buffer size in ms.
|
||||||
|
-f <maximum fps> - Specify maximum framerate.
|
||||||
|
-s <scale> - Specify scale.
|
||||||
|
-b <bitrate in bps> - Specify bitrate.
|
||||||
|
-m <monitor id> - Specify monitor id.
|
||||||
|
-d <debug mode> - 0 = off, 1 = no streaming, 2 = with streaming.
|
||||||
|
-i, -?, -h - Display usage information
|
||||||
|
-v - Print the installed version of ZoneMinder
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/msg.h>
|
||||||
|
|
||||||
|
#include "zm.h"
|
||||||
|
#include "zm_db.h"
|
||||||
|
#include "zm_user.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
#include "zm_stream.h"
|
||||||
|
|
||||||
|
// Possible command-line options
|
||||||
|
#define OPTIONS "e:o:u:f:s:b:m:d:i:?:h:v"
|
||||||
|
|
||||||
|
// Default ZMS values
|
||||||
|
#define ZMS_DEFAULT_DEBUG 0
|
||||||
|
#define ZMS_DEFAULT_ID 1
|
||||||
|
#define ZMS_DEFAULT_BITRATE 100000
|
||||||
|
#define ZMS_DEFAULT_SCALE 100
|
||||||
|
#define ZMS_DEFAULT_MODE "mpeg"
|
||||||
|
#define ZMS_DEFAULT_FORMAT "asf"
|
||||||
|
#define ZMS_DEFAULT_FPS 25.0
|
||||||
|
#define ZMS_DEFAULT_BUFFER 1000
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
self = argv[0];
|
||||||
|
// Set initial values to the default values
|
||||||
|
int debug = ZMS_DEFAULT_DEBUG;
|
||||||
|
int id = ZMS_DEFAULT_ID;
|
||||||
|
int bitrate = ZMS_DEFAULT_BITRATE;
|
||||||
|
int scale = ZMS_DEFAULT_SCALE;
|
||||||
|
char mode[32];
|
||||||
|
sprintf(mode, "%s", ZMS_DEFAULT_MODE);
|
||||||
|
char format[32];
|
||||||
|
sprintf(format, "%s", ZMS_DEFAULT_FORMAT);
|
||||||
|
double maxfps = ZMS_DEFAULT_FPS;
|
||||||
|
int buffer = ZMS_DEFAULT_BUFFER;
|
||||||
|
|
||||||
|
// Parse command-line options
|
||||||
|
int arg;
|
||||||
|
while ((arg = getopt(argc, argv, OPTIONS)) != -1) {
|
||||||
|
switch (arg) {
|
||||||
|
case 'e':
|
||||||
|
sprintf(mode, "%s", optarg);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
sprintf(format, "%s", optarg);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
buffer = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
maxfps = atof(optarg);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
scale = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
bitrate = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
id = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
debug = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
case 'i':
|
||||||
|
case '?':
|
||||||
|
printf("-e <mode> : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE);
|
||||||
|
printf("-o <format> : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT);
|
||||||
|
printf("-u <buffer size> : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER);
|
||||||
|
printf("-f <maximum fps> : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS);
|
||||||
|
printf("-s <scale> : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE);
|
||||||
|
printf("-b <bitrate in bps> : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE);
|
||||||
|
printf("-m <monitor id> : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID);
|
||||||
|
printf("-d <debug mode> : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n");
|
||||||
|
printf("-i or -? or -h: This information\n");
|
||||||
|
printf("-v : This installed version of ZoneMinder\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
case 'v':
|
||||||
|
std::cout << ZM_VERSION << "\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stream type
|
||||||
|
StreamBase::StreamType streamtype;
|
||||||
|
if (!strcasecmp("raw", mode))
|
||||||
|
streamtype = MonitorStream::STREAM_RAW;
|
||||||
|
else if (!strcasecmp("mpeg", mode))
|
||||||
|
streamtype = MonitorStream::STREAM_MPEG;
|
||||||
|
else if (!strcasecmp("jpg", mode))
|
||||||
|
streamtype = MonitorStream::STREAM_JPEG;
|
||||||
|
else if (!strcasecmp("single", mode))
|
||||||
|
streamtype = MonitorStream::STREAM_SINGLE;
|
||||||
|
else if (!strcasecmp("zip", mode))
|
||||||
|
streamtype = MonitorStream::STREAM_ZIP;
|
||||||
|
else
|
||||||
|
streamtype = MonitorStream::STREAM_MPEG;
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
// Show stream parameters
|
||||||
|
printf("Stream parameters:\n");
|
||||||
|
switch (streamtype) {
|
||||||
|
case MonitorStream::STREAM_MPEG:
|
||||||
|
printf("Output mode (-e) = %s\n", "mpeg");
|
||||||
|
printf("Output format (-o) = %s\n", format);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Output mode (-e) = %s\n", mode);
|
||||||
|
}
|
||||||
|
printf("Buffer size (-u) = %d ms\n", buffer);
|
||||||
|
printf("Maximum FPS (-f) = %lf FPS\n", maxfps);
|
||||||
|
printf("Scale (-s) = %d%%\n", scale);
|
||||||
|
printf("Bitrate (-b) = %d bps\n", bitrate);
|
||||||
|
printf("Monitor Id (-m) = %d\n", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
// Set ZM debugger to print to stdout
|
||||||
|
printf("Setting up ZoneMinder debugger to print to stdout...");
|
||||||
|
setenv("ZM_DBG_PRINT", "1", 1);
|
||||||
|
printf("Done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading ZM configurations
|
||||||
|
printf("Loading ZoneMinder configurations...");
|
||||||
|
zmLoadConfig();
|
||||||
|
printf("Done.\n");
|
||||||
|
|
||||||
|
logInit("zmstreamer");
|
||||||
|
|
||||||
|
ssedetect();
|
||||||
|
|
||||||
|
// Setting stream parameters
|
||||||
|
MonitorStream stream;
|
||||||
|
stream.setStreamScale(scale); // default = 100 (scale)
|
||||||
|
stream.setStreamReplayRate(100); // default = 100 (rate)
|
||||||
|
stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps)
|
||||||
|
if (debug) stream.setStreamTTL(1);
|
||||||
|
else stream.setStreamTTL(0); // default = 0 (ttl)
|
||||||
|
stream.setStreamQueue(0); // default = 0 (connkey)
|
||||||
|
stream.setStreamBuffer(buffer); // default = 0 (buffer)
|
||||||
|
stream.setStreamStart(id); // default = 0 (monitor_id)
|
||||||
|
stream.setStreamType(streamtype);
|
||||||
|
if (streamtype == MonitorStream::STREAM_MPEG) {
|
||||||
|
#if HAVE_LIBAVCODEC
|
||||||
|
if (debug) printf("HAVE_LIBAVCODEC is set\n");
|
||||||
|
stream.setStreamFormat(format); // default = "" (format)
|
||||||
|
stream.setStreamBitrate(bitrate); // default = 100000 (bitrate)
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n");
|
||||||
|
logTerm();
|
||||||
|
zmDbClose();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug != 1) {
|
||||||
|
if (debug) printf("Running stream...");
|
||||||
|
|
||||||
|
// Output headers
|
||||||
|
fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION);
|
||||||
|
time_t now = time(0);
|
||||||
|
char date_string[64];
|
||||||
|
strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now));
|
||||||
|
fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n");
|
||||||
|
fprintf(stdout, "Last-Modified: %s\r\n", date_string);
|
||||||
|
fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n");
|
||||||
|
fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n");
|
||||||
|
fprintf(stdout, "Pragma: no-cache\r\n");
|
||||||
|
|
||||||
|
// Run stream
|
||||||
|
stream.runStream();
|
||||||
|
}
|
||||||
|
if (debug) printf("Done.\n");
|
||||||
|
|
||||||
|
logTerm();
|
||||||
|
zmDbClose();
|
||||||
|
|
||||||
|
return (EXIT_SUCCESS);
|
||||||
|
}
|
1448
src/zmu.cpp
1448
src/zmu.cpp
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue