Bug 264 - Added support for zipped raw images.
git-svn-id: http://svn.zoneminder.com/svn/zm/trunk@1847 e3e1d417-86f3-4887-817a-d78f3d33393fpull/27/merge
parent
a6fca601a2
commit
d4e16016ba
|
@ -293,7 +293,7 @@ bool Image::WriteJpeg( const char *filename, int quality_override ) const
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Image::DecodeJpeg( JOCTET *inbuffer, int inbuffer_size )
|
bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size )
|
||||||
{
|
{
|
||||||
struct jpeg_decompress_struct *cinfo = jpg_dcinfo;
|
struct jpeg_decompress_struct *cinfo = jpg_dcinfo;
|
||||||
|
|
||||||
|
@ -397,6 +397,34 @@ bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_over
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Image::Unzip( const Bytef *inbuffer, unsigned long inbuffer_size )
|
||||||
|
{
|
||||||
|
unsigned long zip_size = size;
|
||||||
|
int result = uncompress( buffer, &zip_size, inbuffer, inbuffer_size );
|
||||||
|
if ( result != Z_OK )
|
||||||
|
{
|
||||||
|
Error(( "Unzip failed, result = %d", result ));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
if ( zip_size != size )
|
||||||
|
{
|
||||||
|
Error(( "Unzip failed, size mismatch, expected %d bytes, got %d", size, zip_size ));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Image::Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level ) const
|
||||||
|
{
|
||||||
|
int result = compress2( outbuffer, outbuffer_size, buffer, size, compression_level );
|
||||||
|
if ( result != Z_OK )
|
||||||
|
{
|
||||||
|
Error(( "Zip failed, result = %d", result ));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
return( true );
|
||||||
|
}
|
||||||
|
|
||||||
bool Image::Crop( int lo_x, int lo_y, int hi_x, int hi_y )
|
bool Image::Crop( int lo_x, int lo_y, int hi_x, int hi_y )
|
||||||
{
|
{
|
||||||
int new_width = (hi_x-lo_x)+1;;
|
int new_width = (hi_x-lo_x)+1;;
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -239,9 +240,12 @@ public:
|
||||||
|
|
||||||
bool ReadJpeg( const char *filename );
|
bool ReadJpeg( const char *filename );
|
||||||
bool WriteJpeg( const char *filename, int quality_override=0 ) const;
|
bool WriteJpeg( const char *filename, int quality_override=0 ) const;
|
||||||
bool DecodeJpeg( JOCTET *inbuffer, int inbuffer_size );
|
bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size );
|
||||||
bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const;
|
bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const;
|
||||||
|
|
||||||
|
bool Unzip( const Bytef *inbuffer, unsigned long inbuffer_size );
|
||||||
|
bool Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level=Z_BEST_SPEED ) const;
|
||||||
|
|
||||||
bool Crop( int lo_x, int lo_y, int hi_y, int hi_y );
|
bool Crop( int lo_x, int lo_y, int hi_y, int hi_y );
|
||||||
|
|
||||||
void Overlay( const Image &image );
|
void Overlay( const Image &image );
|
||||||
|
|
|
@ -348,7 +348,7 @@ static void term_source (j_decompress_ptr cinfo)
|
||||||
* for closing it after finishing decompression.
|
* for closing it after finishing decompression.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void jpeg_mem_src( j_decompress_ptr cinfo, JOCTET *inbuffer, int inbuffer_size )
|
void jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size )
|
||||||
{
|
{
|
||||||
mem_src_ptr src;
|
mem_src_ptr src;
|
||||||
|
|
||||||
|
|
|
@ -36,5 +36,5 @@ void zm_jpeg_error_exit( j_common_ptr cinfo );
|
||||||
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level );
|
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level );
|
||||||
|
|
||||||
// Prototypes for memory compress/decompression object */
|
// Prototypes for memory compress/decompression object */
|
||||||
void jpeg_mem_src(j_decompress_ptr cinfo, JOCTET *inbuffer, int inbuffer_size );
|
void jpeg_mem_src(j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size );
|
||||||
void jpeg_mem_dest(j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size );
|
void jpeg_mem_dest(j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size );
|
||||||
|
|
|
@ -1748,6 +1748,92 @@ void Monitor::StreamImagesRaw( int scale, int maxfps, time_t ttl )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Monitor::StreamImagesZip( int scale, int maxfps, time_t ttl )
|
||||||
|
{
|
||||||
|
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" );
|
||||||
|
|
||||||
|
int fps = int(GetFPS());
|
||||||
|
if ( !fps )
|
||||||
|
fps = 5;
|
||||||
|
|
||||||
|
int min_fps = 1;
|
||||||
|
int max_fps = maxfps;
|
||||||
|
int base_fps = int(GetFPS());
|
||||||
|
int effective_fps = base_fps;
|
||||||
|
|
||||||
|
int frame_mod = 1;
|
||||||
|
// Min frame repeat?
|
||||||
|
while( effective_fps > max_fps )
|
||||||
|
{
|
||||||
|
effective_fps /= 2;
|
||||||
|
frame_mod *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug( 1, ( "BFPS:%d, EFPS:%d, FM:%d", base_fps, effective_fps, frame_mod ));
|
||||||
|
|
||||||
|
int last_read_index = image_buffer_count;
|
||||||
|
|
||||||
|
time_t stream_start_time;
|
||||||
|
time( &stream_start_time );
|
||||||
|
|
||||||
|
int frame_count = 0;
|
||||||
|
struct timeval base_time;
|
||||||
|
struct DeltaTimeval delta_time;
|
||||||
|
unsigned long img_buffer_size = 0;
|
||||||
|
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
Image scaled_image;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
if ( feof( stdout ) || ferror( stdout ) )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( last_read_index != shared_data->last_write_index )
|
||||||
|
{
|
||||||
|
last_read_index = shared_data->last_write_index;
|
||||||
|
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) )
|
||||||
|
{
|
||||||
|
// Send the next frame
|
||||||
|
int index = shared_data->last_write_index%image_buffer_count;
|
||||||
|
//Info(( "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ));
|
||||||
|
Snapshot *snap = &image_buffer[index];
|
||||||
|
Image *snap_image = snap->image;
|
||||||
|
|
||||||
|
if ( scale != 100 )
|
||||||
|
{
|
||||||
|
scaled_image.Assign( *snap_image );
|
||||||
|
|
||||||
|
scaled_image.Scale( scale );
|
||||||
|
|
||||||
|
snap_image = &scaled_image;
|
||||||
|
}
|
||||||
|
if ( !config.timestamp_on_capture )
|
||||||
|
{
|
||||||
|
TimestampImage( snap_image, snap->timestamp->tv_sec );
|
||||||
|
}
|
||||||
|
snap_image->Zip( img_buffer, &img_buffer_size );
|
||||||
|
|
||||||
|
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size );
|
||||||
|
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
||||||
|
fwrite( img_buffer, img_buffer_size, 1, stdout );
|
||||||
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
|
|
||||||
|
if ( ttl )
|
||||||
|
{
|
||||||
|
time_t now = time ( 0 );
|
||||||
|
if ( (now - stream_start_time) > ttl )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame_count++;
|
||||||
|
}
|
||||||
|
usleep( ZM_SAMPLE_RATE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Monitor::SingleImage( int scale)
|
void Monitor::SingleImage( int scale)
|
||||||
{
|
{
|
||||||
int last_read_index = shared_data->last_write_index;
|
int last_read_index = shared_data->last_write_index;
|
||||||
|
@ -1799,6 +1885,33 @@ void Monitor::SingleImageRaw( int scale)
|
||||||
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
|
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Monitor::SingleImageZip( int scale)
|
||||||
|
{
|
||||||
|
int last_read_index = shared_data->last_write_index;
|
||||||
|
unsigned long img_buffer_size = 0;
|
||||||
|
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||||
|
Image scaled_image;
|
||||||
|
int index = shared_data->last_write_index%image_buffer_count;
|
||||||
|
Snapshot *snap = &image_buffer[index];
|
||||||
|
Image *snap_image = snap->image;
|
||||||
|
|
||||||
|
if ( scale != 100 )
|
||||||
|
{
|
||||||
|
scaled_image.Assign( *snap_image );
|
||||||
|
scaled_image.Scale( scale );
|
||||||
|
snap_image = &scaled_image;
|
||||||
|
}
|
||||||
|
if ( !config.timestamp_on_capture )
|
||||||
|
{
|
||||||
|
TimestampImage( snap_image, snap->timestamp->tv_sec );
|
||||||
|
}
|
||||||
|
snap_image->Zip( img_buffer, &img_buffer_size );
|
||||||
|
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size );
|
||||||
|
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
||||||
|
fwrite( img_buffer, img_buffer_size, 1, stdout );
|
||||||
|
}
|
||||||
|
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
|
|
||||||
void Monitor::StreamMpeg( const char *format, int scale, int maxfps, int bitrate )
|
void Monitor::StreamMpeg( const char *format, int scale, int maxfps, int bitrate )
|
||||||
|
|
|
@ -266,8 +266,10 @@ public:
|
||||||
static Monitor *Load( int id, bool load_zones=false, Purpose purpose=QUERY );
|
static Monitor *Load( int id, bool load_zones=false, Purpose purpose=QUERY );
|
||||||
void StreamImages( int scale=100, int maxfps=10, time_t ttl=0 );
|
void StreamImages( int scale=100, int maxfps=10, time_t ttl=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 SingleImage( int scale=100 );
|
void SingleImage( int scale=100 );
|
||||||
void SingleImageRaw( int scale=100 );
|
void SingleImageRaw( 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
|
||||||
|
|
|
@ -358,6 +358,13 @@ int RemoteCamera::GetResponse()
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
}
|
}
|
||||||
|
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||||
|
{
|
||||||
|
// Single image
|
||||||
|
mode = SINGLE_IMAGE;
|
||||||
|
format = X_RGBZ;
|
||||||
|
state = CONTENT;
|
||||||
|
}
|
||||||
else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
|
else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
|
||||||
{
|
{
|
||||||
// Image stream, so start processing
|
// Image stream, so start processing
|
||||||
|
@ -445,6 +452,10 @@ int RemoteCamera::GetResponse()
|
||||||
{
|
{
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
}
|
}
|
||||||
|
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||||
|
{
|
||||||
|
format = X_RGBZ;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error(( "Found unsupported content type '%s'", content_type ));
|
Error(( "Found unsupported content type '%s'", content_type ));
|
||||||
|
@ -771,6 +782,13 @@ int RemoteCamera::GetResponse()
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
state = CONTENT;
|
state = CONTENT;
|
||||||
}
|
}
|
||||||
|
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||||
|
{
|
||||||
|
// Single image
|
||||||
|
mode = SINGLE_IMAGE;
|
||||||
|
format = X_RGBZ;
|
||||||
|
state = CONTENT;
|
||||||
|
}
|
||||||
else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
|
else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
|
||||||
{
|
{
|
||||||
// Image stream, so start processing
|
// Image stream, so start processing
|
||||||
|
@ -924,6 +942,10 @@ int RemoteCamera::GetResponse()
|
||||||
{
|
{
|
||||||
format = X_RGB;
|
format = X_RGB;
|
||||||
}
|
}
|
||||||
|
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||||
|
{
|
||||||
|
format = X_RGBZ;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error(( "Found unsupported content type '%s'", content_type ));
|
Error(( "Found unsupported content type '%s'", content_type ));
|
||||||
|
@ -1062,6 +1084,7 @@ int RemoteCamera::PostCapture( Image &image )
|
||||||
{
|
{
|
||||||
if ( !image.DecodeJpeg( buffer.Extract( content_length ), content_length ) )
|
if ( !image.DecodeJpeg( buffer.Extract( content_length ), content_length ) )
|
||||||
{
|
{
|
||||||
|
Disconnect();
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1077,6 +1100,16 @@ int RemoteCamera::PostCapture( Image &image )
|
||||||
image.Assign( width, height, colours, buffer );
|
image.Assign( width, height, colours, buffer );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case X_RGBZ :
|
||||||
|
{
|
||||||
|
if ( !image.Unzip( buffer.Extract( content_length ), content_length ) )
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
image.Assign( width, height, colours, buffer );
|
||||||
|
break;
|
||||||
|
}
|
||||||
default :
|
default :
|
||||||
{
|
{
|
||||||
Error(( "Unexpected image format encountered" ));
|
Error(( "Unexpected image format encountered" ));
|
||||||
|
|
|
@ -47,7 +47,7 @@ protected:
|
||||||
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 } format;
|
enum { UNDEF, JPEG, X_RGB, X_RGBZ } format;
|
||||||
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -48,7 +48,7 @@ bool ValidateAccess( User *user, int mon_id )
|
||||||
|
|
||||||
int main( int argc, const char *argv[] )
|
int main( int argc, const char *argv[] )
|
||||||
{
|
{
|
||||||
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, 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 id = 0;
|
int id = 0;
|
||||||
int event = 0;
|
int event = 0;
|
||||||
|
@ -98,6 +98,7 @@ int main( int argc, const char *argv[] )
|
||||||
{
|
{
|
||||||
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG;
|
||||||
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
mode = !strcmp( value, "raw" )?ZMS_RAW:mode;
|
||||||
|
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
||||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
||||||
}
|
}
|
||||||
else if ( !strcmp( name, "monitor" ) )
|
else if ( !strcmp( name, "monitor" ) )
|
||||||
|
@ -224,6 +225,10 @@ int main( int argc, const char *argv[] )
|
||||||
{
|
{
|
||||||
monitor->StreamImagesRaw( scale, maxfps, ttl );
|
monitor->StreamImagesRaw( scale, maxfps, ttl );
|
||||||
}
|
}
|
||||||
|
else if ( mode == ZMS_ZIP )
|
||||||
|
{
|
||||||
|
monitor->StreamImagesZip( scale, maxfps, ttl );
|
||||||
|
}
|
||||||
else if ( mode == ZMS_SINGLE )
|
else if ( mode == ZMS_SINGLE )
|
||||||
{
|
{
|
||||||
monitor->SingleImage( scale );
|
monitor->SingleImage( scale );
|
||||||
|
|
Loading…
Reference in New Issue