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 );
|
||||
}
|
||||
|
||||
bool Image::DecodeJpeg( JOCTET *inbuffer, int inbuffer_size )
|
||||
bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size )
|
||||
{
|
||||
struct jpeg_decompress_struct *cinfo = jpg_dcinfo;
|
||||
|
||||
|
@ -397,6 +397,34 @@ bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_over
|
|||
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 )
|
||||
{
|
||||
int new_width = (hi_x-lo_x)+1;;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#include <zlib.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -239,9 +240,12 @@ public:
|
|||
|
||||
bool ReadJpeg( const char *filename );
|
||||
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 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 );
|
||||
|
||||
void Overlay( const Image &image );
|
||||
|
|
|
@ -348,7 +348,7 @@ static void term_source (j_decompress_ptr cinfo)
|
|||
* 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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
||||
// 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 );
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 );
|
||||
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 StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
void SingleImageZip( int scale=100 );
|
||||
#if HAVE_LIBAVCODEC
|
||||
void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 );
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
|
@ -358,6 +358,13 @@ int RemoteCamera::GetResponse()
|
|||
format = X_RGB;
|
||||
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" ) )
|
||||
{
|
||||
// Image stream, so start processing
|
||||
|
@ -445,6 +452,10 @@ int RemoteCamera::GetResponse()
|
|||
{
|
||||
format = X_RGB;
|
||||
}
|
||||
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||
{
|
||||
format = X_RGBZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(( "Found unsupported content type '%s'", content_type ));
|
||||
|
@ -771,6 +782,13 @@ int RemoteCamera::GetResponse()
|
|||
format = X_RGB;
|
||||
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" ) )
|
||||
{
|
||||
// Image stream, so start processing
|
||||
|
@ -924,6 +942,10 @@ int RemoteCamera::GetResponse()
|
|||
{
|
||||
format = X_RGB;
|
||||
}
|
||||
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
|
||||
{
|
||||
format = X_RGBZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
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 ) )
|
||||
{
|
||||
Disconnect();
|
||||
return( -1 );
|
||||
}
|
||||
break;
|
||||
|
@ -1077,6 +1100,16 @@ int RemoteCamera::PostCapture( Image &image )
|
|||
image.Assign( width, height, colours, buffer );
|
||||
break;
|
||||
}
|
||||
case X_RGBZ :
|
||||
{
|
||||
if ( !image.Unzip( buffer.Extract( content_length ), content_length ) )
|
||||
{
|
||||
Disconnect();
|
||||
return( -1 );
|
||||
}
|
||||
image.Assign( width, height, colours, buffer );
|
||||
break;
|
||||
}
|
||||
default :
|
||||
{
|
||||
Error(( "Unexpected image format encountered" ));
|
||||
|
|
|
@ -47,7 +47,7 @@ protected:
|
|||
int sd;
|
||||
Buffer buffer;
|
||||
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;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -48,7 +48,7 @@ bool ValidateAccess( User *user, int mon_id )
|
|||
|
||||
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] = "";
|
||||
int id = 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, "raw" )?ZMS_RAW:mode;
|
||||
mode = !strcmp( value, "zip" )?ZMS_ZIP:mode;
|
||||
mode = !strcmp( value, "single" )?ZMS_SINGLE:mode;
|
||||
}
|
||||
else if ( !strcmp( name, "monitor" ) )
|
||||
|
@ -224,6 +225,10 @@ int main( int argc, const char *argv[] )
|
|||
{
|
||||
monitor->StreamImagesRaw( scale, maxfps, ttl );
|
||||
}
|
||||
else if ( mode == ZMS_ZIP )
|
||||
{
|
||||
monitor->StreamImagesZip( scale, maxfps, ttl );
|
||||
}
|
||||
else if ( mode == ZMS_SINGLE )
|
||||
{
|
||||
monitor->SingleImage( scale );
|
||||
|
|
Loading…
Reference in New Issue