This commit is contained in:
Isaac Connor 2017-11-13 12:14:57 -05:00
parent 0e799233d2
commit aee2b148f0
5 changed files with 88 additions and 143 deletions

View File

@ -372,7 +372,6 @@ Monitor::Monitor(
mem_size = sizeof(SharedData)
+ sizeof(TriggerData)
+ sizeof(VideoStoreData) //Information to pass back to the capture process
+ (image_buffer_count*sizeof(struct timeval))
+ (image_buffer_count*camera->ImageSize())
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
@ -553,19 +552,17 @@ bool Monitor::connect() {
shared_data = (SharedData *)mem_ptr;
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
video_store_data = (VideoStoreData *)((char *)trigger_data + sizeof(TriggerData));
struct timeval *shared_timestamps = (struct timeval *)((char *)video_store_data + sizeof(VideoStoreData));
unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval)));
unsigned char *shared_images = (unsigned char *)((char *)video_store_data + sizeof(VideoStoreData));
if ( ((unsigned long)shared_images % 64) != 0 ) {
/* Align images buffer to nearest 64 byte boundary */
Debug(3,"Aligning shared memory images to the next 64 byte boundary");
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
}
Debug(3, "Allocating %d image buffers", image_buffer_count );
image_buffer = new Snapshot[image_buffer_count];
image_buffer = new ZMPacket[image_buffer_count];
for ( int i = 0; i < image_buffer_count; i++ ) {
image_buffer[i].timestamp = &(shared_timestamps[i]);
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
}
@ -573,16 +570,14 @@ bool Monitor::connect() {
/* Four field motion adaptive deinterlacing in use */
/* Allocate a buffer for the next image */
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval;
}
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
// Size of pre event buffer must be greater than pre_event_count
// if alarm_frame_count > 1, because in this case the buffer contains
// alarmed images that must be discarded when event is created
pre_event_buffer_count = pre_event_count + alarm_frame_count - 1;
pre_event_buffer = new Snapshot[pre_event_buffer_count];
pre_event_buffer = new ZMPacket[pre_event_buffer_count];
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
pre_event_buffer[i].timestamp = new struct timeval;
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
}
}
@ -611,7 +606,6 @@ Monitor::~Monitor() {
if ( (deinterlacing & 0xff) == 4) {
delete next_buffer.image;
delete next_buffer.timestamp;
}
for ( int i = 0; i < image_buffer_count; i++ ) {
delete image_buffer[i].image;
@ -636,7 +630,6 @@ Monitor::~Monitor() {
if ( analysis_fps ) {
for ( int i = 0; i < pre_event_buffer_count; i++ ) {
delete pre_event_buffer[i].image;
delete pre_event_buffer[i].timestamp;
}
delete[] pre_event_buffer;
}
@ -718,7 +711,7 @@ int Monitor::GetImage( int index, int scale ) {
Image *image;
// If we are going to be modifying the snapshot before writing, then we need to copy it
if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) {
Snapshot *snap = &image_buffer[index];
ZMPacket *snap = &image_buffer[index];
Image *snap_image = snap->image;
alarm_image.Assign( *snap_image );
@ -731,7 +724,7 @@ int Monitor::GetImage( int index, int scale ) {
}
if ( !config.timestamp_on_capture ) {
TimestampImage( &alarm_image, snap->timestamp );
TimestampImage( &alarm_image, &snap->timestamp );
}
image = &alarm_image;
} else {
@ -753,13 +746,13 @@ struct timeval Monitor::GetTimestamp( int index ) const {
}
if ( index != image_buffer_count ) {
Snapshot *snap = &image_buffer[index];
ZMPacket *snap = &image_buffer[index];
return( *(snap->timestamp) );
return snap->timestamp;
} else {
static struct timeval null_tv = { 0, 0 };
return( null_tv );
return null_tv;
}
}
@ -778,29 +771,29 @@ unsigned int Monitor::GetLastEvent() const {
double Monitor::GetFPS() const {
int index1 = shared_data->last_write_index;
if ( index1 == image_buffer_count ) {
return( 0.0 );
return 0.0;
}
Snapshot *snap1 = &image_buffer[index1];
if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) {
return( 0.0 );
ZMPacket *snap1 = &image_buffer[index1];
if ( !snap1->timestamp.tv_sec ) {
return 0.0;
}
struct timeval time1 = *snap1->timestamp;
struct timeval time1 = snap1->timestamp;
int image_count = image_buffer_count;
int index2 = (index1+1)%image_buffer_count;
if ( index2 == image_buffer_count ) {
return( 0.0 );
return 0.0;
}
Snapshot *snap2 = &image_buffer[index2];
while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) {
ZMPacket *snap2 = &image_buffer[index2];
while ( !snap2->timestamp.tv_sec ) {
if ( index1 == index2 ) {
return( 0.0 );
return 0.0;
}
index2 = (index2+1)%image_buffer_count;
snap2 = &image_buffer[index2];
image_count--;
}
struct timeval time2 = *snap2->timestamp;
struct timeval time2 = snap2->timestamp;
double time_diff = tvDiffSec( time2, time1 );
@ -808,9 +801,9 @@ double Monitor::GetFPS() const {
if ( curr_fps < 0.0 ) {
//Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count );
return( 0.0 );
return 0.0;
}
return( curr_fps );
return curr_fps;
}
useconds_t Monitor::GetAnalysisRate() {
@ -1028,7 +1021,7 @@ void Monitor::DumpZoneImage( const char *zone_string ) {
if ( ( (!staticConfig.SERVER_ID) || ( staticConfig.SERVER_ID == server_id ) ) && mem_ptr ) {
Debug(3, "Trying to load from local zmc");
int index = shared_data->last_write_index;
Snapshot *snap = &image_buffer[index];
ZMPacket *snap = &image_buffer[index];
zone_image = new Image( *snap->image );
} else {
Debug(3, "Trying to load from event");
@ -1216,8 +1209,9 @@ bool Monitor::Analyse() {
index = shared_data->last_write_index%image_buffer_count;
}
Snapshot *snap = &image_buffer[index];
struct timeval *timestamp = snap->timestamp;
ZMPacket *snap = &image_buffer[index];
struct timeval *timestamp = &snap->timestamp;
Debug(2,timeval_to_string( *timestamp ) );
Image *snap_image = snap->image;
if ( shared_data->action ) {
@ -1267,7 +1261,6 @@ bool Monitor::Analyse() {
if ( static_undef ) {
// Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary.
static_undef = false;
timestamps = new struct timeval *[pre_event_count];
images = new Image *[pre_event_count];
last_signal = shared_data->signal;
}
@ -1419,56 +1412,6 @@ bool Monitor::Analyse() {
if ( state == IDLE ) {
shared_data->state = state = TAPE;
}
//if ( config.overlap_timed_events )
if ( false ) {
int pre_index;
int pre_event_images = pre_event_count;
if ( analysis_fps ) {
// If analysis fps is set,
// compute the index for pre event images in the dedicated buffer
pre_index = image_count%pre_event_buffer_count;
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) {
pre_index = (pre_index + 1)%pre_event_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
} else {
// If analysis fps is not set (analysis performed at capturing framerate),
// compute the index for pre event images in the capturing buffer
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) {
pre_index = (pre_index + 1)%image_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
}
if ( pre_event_images ) {
if ( analysis_fps ) {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = pre_event_buffer[pre_index].timestamp;
images[i] = pre_event_buffer[pre_index].image;
pre_index = (pre_index + 1)%pre_event_buffer_count;
}
} else {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = image_buffer[pre_index].timestamp;
images[i] = image_buffer[pre_index].image;
pre_index = (pre_index + 1)%image_buffer_count;
}
}
event->AddFrames( pre_event_images, images, timestamps );
}
} // end if false or config.overlap_timed_events
} // end if ! event
}
if ( score ) {
@ -1487,13 +1430,13 @@ bool Monitor::Analyse() {
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) {
while ( pre_event_images && !pre_event_buffer[pre_index].timestamp.tv_sec ) {
pre_index = (pre_index + 1)%pre_event_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
event = new Event( this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap );
event = new Event( this, pre_event_buffer[pre_index].timestamp, cause, noteSetMap );
} else {
// If analysis fps is not set (analysis performed at capturing framerate),
// compute the index for pre event images in the capturing buffer
@ -1504,13 +1447,13 @@ bool Monitor::Analyse() {
// Seek forward the next filled slot in to the buffer (oldest data)
// from the current position
while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) {
while ( pre_event_images && !image_buffer[pre_index].timestamp.tv_sec ) {
pre_index = (pre_index + 1)%image_buffer_count;
// Slot is empty, removing image from counter
pre_event_images--;
}
event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap );
event = new Event( this, image_buffer[pre_index].timestamp, cause, noteSetMap );
}
shared_data->last_event_id = event->Id();
//set up video store data
@ -1522,13 +1465,13 @@ bool Monitor::Analyse() {
if ( pre_event_images ) {
if ( analysis_fps ) {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = pre_event_buffer[pre_index].timestamp;
timestamps[i] = &pre_event_buffer[pre_index].timestamp;
images[i] = pre_event_buffer[pre_index].image;
pre_index = (pre_index + 1)%pre_event_buffer_count;
}
} else {
for ( int i = 0; i < pre_event_images; i++ ) {
timestamps[i] = image_buffer[pre_index].timestamp;
timestamps[i] = &image_buffer[pre_index].timestamp;
images[i] = image_buffer[pre_index].image;
pre_index = (pre_index + 1)%image_buffer_count;
}
@ -1667,7 +1610,7 @@ bool Monitor::Analyse() {
// If analysis fps is set, add analysed image to dedicated pre event buffer
int pre_index = image_count%pre_event_buffer_count;
pre_event_buffer[pre_index].image->Assign(*snap->image);
memcpy( pre_event_buffer[pre_index].timestamp, snap->timestamp, sizeof(struct timeval) );
pre_event_buffer[pre_index].timestamp = snap->timestamp;
}
image_count++;
@ -2862,8 +2805,7 @@ int Monitor::Capture() {
unsigned int index = image_count % image_buffer_count;
Image* capture_image = image_buffer[index].image;
ZMPacket packet;
packet.set_image(capture_image);
ZMPacket *packet = &image_buffer[index];
int captureResult = 0;
unsigned int deinterlacing_value = deinterlacing & 0xff;
@ -2874,14 +2816,17 @@ int Monitor::Capture() {
}
/* Capture a new next image */
captureResult = camera->Capture(packet);
captureResult = camera->Capture(*packet);
gettimeofday( &packet->timestamp, NULL );
if ( FirstCapture ) {
FirstCapture = 0;
return 0;
}
} else {
captureResult = camera->Capture(packet);
captureResult = camera->Capture(*packet);
gettimeofday( &packet->timestamp, NULL );
Debug(2,timeval_to_string( packet->timestamp ) );
if ( captureResult < 0 ) {
// Unable to capture image for temporary reason
// Fake a signal loss image
@ -2928,7 +2873,7 @@ int Monitor::Capture() {
if ( (index == shared_data->last_read_index) && (function > MONITOR) ) {
Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count );
time_t now = time(0);
double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec);
double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp.tv_sec);
time_t last_read_delta = now - shared_data->last_read_time;
if ( last_read_delta > (image_buffer_count/approxFps) ) {
Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta )
@ -2940,7 +2885,7 @@ int Monitor::Capture() {
capture_image->MaskPrivacy( privacy_bitmask );
if ( config.timestamp_on_capture ) {
TimestampImage( capture_image, &packet.timestamp );
TimestampImage( capture_image, &packet->timestamp );
}
int video_stream_id = camera->get_VideoStreamId();
@ -2955,7 +2900,7 @@ int Monitor::Capture() {
Debug(2, "Have videostore already?");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio.
int ret = videoStore->writePacket( &packet );
int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}
@ -2998,25 +2943,25 @@ int Monitor::Capture() {
// Buffer video packets, since we are not recording.
// All audio packets are keyframes, so only if it's a video keyframe
if ( ( packet.packet.stream_index == video_stream_id ) && ( packet.keyframe ) ) {
if ( ( packet->packet.stream_index == video_stream_id ) && ( packet->keyframe ) ) {
packetqueue.clearQueue( this->GetPreEventCount(), video_stream_id );
}
// The following lines should ensure that the queue always begins with a video keyframe
if ( packet.packet.stream_index == camera->get_AudioStreamId() ) {
if ( packet->packet.stream_index == camera->get_AudioStreamId() ) {
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
if ( record_audio && packetqueue.size() ) {
// if it's audio, and we are doing audio, and there is already something in the queue
packetqueue.queuePacket( &packet );
packetqueue.queuePacket( packet );
}
} else if ( packet.packet.stream_index == video_stream_id ) {
if ( packet.keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( &packet );
} else if ( packet->packet.stream_index == video_stream_id ) {
if ( packet->keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( packet );
} // end if audio or video
} // end if recording or not
if ( videoStore ) {
//Write the packet to our video store, it will be smart enough to know what to do
int ret = videoStore->writePacket( &packet );
int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("problem writing packet");
}
@ -3025,7 +2970,7 @@ int Monitor::Capture() {
shared_data->signal = CheckSignal(capture_image);
shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
shared_data->last_write_time = image_buffer[index].timestamp.tv_sec;
image_count++;
@ -3034,7 +2979,7 @@ int Monitor::Capture() {
if ( !captureResult ) {
gettimeofday( &now, NULL );
} else {
now.tv_sec = image_buffer[index].timestamp->tv_sec;
now.tv_sec = image_buffer[index].timestamp.tv_sec;
}
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
@ -3355,6 +3300,6 @@ int Monitor::PostCapture() {
}
Monitor::Orientation Monitor::getOrientation() const { return orientation; }
Monitor::Snapshot *Monitor::getSnapshot() {
ZMPacket *Monitor::getSnapshot() {
return &image_buffer[ shared_data->last_write_index%image_buffer_count ];
}

View File

@ -153,13 +153,6 @@ protected:
char trigger_showtext[256];
} TriggerData;
/* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */
struct Snapshot {
struct timeval *timestamp;
Image *image;
void* padding;
};
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
typedef struct {
@ -197,7 +190,6 @@ protected:
int last_state;
int last_event_id;
public:
MonitorLink( int p_id, const char *p_name );
~MonitorLink();
@ -313,9 +305,9 @@ protected:
TriggerData *trigger_data;
VideoStoreData *video_store_data;
Snapshot *image_buffer;
Snapshot next_buffer; /* Used by four field deinterlacing */
Snapshot *pre_event_buffer;
ZMPacket *image_buffer;
ZMPacket next_buffer; /* Used by four field deinterlacing */
ZMPacket *pre_event_buffer;
Camera *camera;
@ -444,7 +436,7 @@ public:
unsigned int GetPreEventCount() const { return pre_event_count; };
State GetState() const;
int GetImage( int index=-1, int scale=100 );
Snapshot *getSnapshot();
ZMPacket *getSnapshot();
struct timeval GetTimestamp( int index=-1 ) const;
void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate();

View File

@ -665,13 +665,13 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
if ( !paused && !delayed ) {
// Send the next frame
Monitor::Snapshot *snap = &monitor->image_buffer[index];
ZMPacket *snap = &monitor->image_buffer[index];
if ( !sendFrame( snap->image, snap->timestamp ) ) {
if ( !sendFrame( snap->image, &snap->timestamp ) ) {
Debug(2, "sendFrame failed, quiting.");
zm_terminate = true;
}
memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) );
last_frame_timestamp = snap->timestamp;
//frame_sent = true;
temp_read_index = temp_write_index;
@ -679,14 +679,14 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
}
if ( buffered_playback ) {
if ( monitor->shared_data->valid ) {
if ( monitor->image_buffer[index].timestamp->tv_sec ) {
if ( monitor->image_buffer[index].timestamp.tv_sec ) {
int temp_index = temp_write_index%temp_image_buffer_count;
Debug( 2, "Storing frame %d", temp_index );
if ( !temp_image_buffer[temp_index].valid ) {
snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index );
temp_image_buffer[temp_index].valid = true;
}
memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) );
temp_image_buffer[temp_index].timestamp = monitor->image_buffer[index].timestamp;
monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality );
temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count );
if ( temp_write_index == temp_read_index ) {
@ -764,7 +764,7 @@ void MonitorStream::SingleImage( int scale ) {
int img_buffer_size = 0;
static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE];
Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot();
ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) {
@ -773,7 +773,7 @@ void MonitorStream::SingleImage( int scale ) {
snap_image = &scaled_image;
}
if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp );
monitor->TimestampImage( snap_image, &snap->timestamp );
}
snap_image->EncodeJpeg( img_buffer, &img_buffer_size );
@ -784,7 +784,7 @@ void MonitorStream::SingleImage( int scale ) {
void MonitorStream::SingleImageRaw( int scale ) {
Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot();
ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) {
@ -793,7 +793,7 @@ void MonitorStream::SingleImageRaw( int scale ) {
snap_image = &scaled_image;
}
if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp );
monitor->TimestampImage( snap_image, &snap->timestamp );
}
fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() );
@ -806,7 +806,7 @@ void MonitorStream::SingleImageZip( int scale ) {
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
Image scaled_image;
Monitor::Snapshot *snap = monitor->getSnapshot();
ZMPacket *snap = monitor->getSnapshot();
Image *snap_image = snap->image;
if ( scale != ZM_SCALE_BASE ) {
@ -815,7 +815,7 @@ void MonitorStream::SingleImageZip( int scale ) {
snap_image = &scaled_image;
}
if ( !config.timestamp_on_capture ) {
monitor->TimestampImage( snap_image, snap->timestamp );
monitor->TimestampImage( snap_image, &snap->timestamp );
}
snap_image->Zip( img_buffer, &img_buffer_size );

View File

@ -29,8 +29,8 @@ ZMPacket::ZMPacket( ) {
image = NULL;
frame = NULL;
av_init_packet( &packet );
packet.size = 0;
gettimeofday( &timestamp, NULL );
packet.size = 0; // So we can detect whether it has been filled.
timestamp = (struct timeval){0};
}
ZMPacket::ZMPacket( Image *i ) {
@ -38,7 +38,7 @@ ZMPacket::ZMPacket( Image *i ) {
image = i;
frame = NULL;
av_init_packet( &packet );
gettimeofday( &timestamp, NULL );
timestamp = (struct timeval){0};
}
ZMPacket::ZMPacket( AVPacket *p ) {
@ -71,6 +71,13 @@ ZMPacket::~ZMPacket() {
//}
}
void ZMPacket::reset() {
zm_av_packet_unref( &packet );
if ( frame ) {
av_frame_free( &frame );
}
}
int ZMPacket::decode( AVCodecContext *ctx ) {
Debug(4, "about to decode video" );

View File

@ -36,7 +36,7 @@ class ZMPacket {
int keyframe;
AVPacket packet; // Input packet, undecoded
AVFrame *frame; // Input image, decoded Theoretically only filled if needed.
Image *image; // Our internal image oject representing this frame
Image *image; // Our internal image object representing this frame
struct timeval timestamp;
public:
AVPacket *av_packet() { return &packet; }
@ -47,6 +47,7 @@ class ZMPacket {
int is_keyframe() { return keyframe; };
int decode( AVCodecContext *ctx );
void reset();
ZMPacket( AVPacket *packet, struct timeval *timestamp );
ZMPacket( AVPacket *packet );
ZMPacket( AVPacket *packet, AVFrame *frame, Image *image );