This commit is contained in:
Isaac Connor 2019-02-25 10:21:43 -05:00
parent 279e0d8bcf
commit 448294f593
11 changed files with 193 additions and 135 deletions

View File

@ -241,7 +241,7 @@ int FfmpegCamera::OpenFfmpeg() {
}
if ( ret < 0 ) {
Warning("Could not set rtsp_transport method '%s'\n", method.c_str());
Warning("Could not set rtsp_transport method '%s'", method.c_str());
}
Debug(1, "Calling avformat_open_input for %s", mPath.c_str());

View File

@ -2082,6 +2082,10 @@ int LocalCamera::Capture( ZMPacket &zm_packet ) {
} /* prime capture */
if ( ! zm_packet.image ) {
zm_packet.image = new Image(width, height, colours, subpixelorder);
}
if ( conversion_type != 0 ) {
Debug(3, "Performing format conversion");

View File

@ -1630,6 +1630,7 @@ bool Monitor::Analyse() {
ZMPacket *snap;
// Is it possible for snap->score to be ! -1?
// get_analysis_packet will lock the packet
while ( ( snap = packetqueue->get_analysis_packet() ) && ( snap->score == -1 ) ) {
unsigned int index = snap->image_index;
Debug(2, "Analysis index (%d), last_Write(%d)", index, shared_data->last_write_index);
@ -1638,7 +1639,6 @@ bool Monitor::Analyse() {
return 0;
}
snap->lock();
packets_processed += 1;
if ( snap->image_index == -1 ) {
@ -2069,7 +2069,6 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
db_mutex.lock();
static char sql[ZM_SQL_SML_BUFSIZ];
db_mutex.lock();
snprintf(sql, sizeof(sql), "SELECT Id, Name FROM Monitors WHERE Id = %d AND Function != 'None' AND Function != 'Monitor' AND Enabled = 1", link_ids[i] );
if ( mysql_query(&dbconn, sql) ) {
db_mutex.unlock();
@ -2177,7 +2176,8 @@ int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose p
int Monitor::Capture() {
static int FirstCapture = 1; // Used in de-interlacing to indicate whether this is the even or odd image
unsigned int index = image_count % image_buffer_count;
unsigned int index = 0;
//image_count % image_buffer_count;
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);
@ -2189,13 +2189,14 @@ int Monitor::Capture() {
shared_data->last_read_index = image_buffer_count;
}
} else {
Debug(2,"Capture: Current write index %d, last read index %d, current (%d)", shared_data->last_write_index, shared_data->last_read_index, index );
Debug(2,"Capture: Last write(capture) index %d, last read(analysis) index %d, current (%d)",
shared_data->last_write_index, shared_data->last_read_index, index );
}
ZMPacket *packet = &image_buffer[index];
Debug(2,"before lock");
ZMPacket *packet = new ZMPacket();
//&image_buffer[index];
Debug(2,"before packet lock");
packet->lock();
Debug(2,"before reset");
packet->reset();
Image* capture_image = packet->image;
int captureResult = 0;
@ -2243,23 +2244,23 @@ int Monitor::Capture() {
if ( packet->packet.stream_index != video_stream_id ) {
Debug(2, "Have audio packet (%d) != videostream_id:(%d) q.vpktcount(%d) event?(%d) ",
packet->packet.stream_index, video_stream_id, packetqueue->video_packet_count, ( event ? 1 : 0 ) );
// Only queue if we have some video packets in there.
mutex.lock();
// Only queue if we have some video packets in there. Should push this logic into packetqueue
//mutex.lock();
if ( packetqueue->video_packet_count || event ) {
// Need to copy it into another ZMPacket.
ZMPacket *audio_packet = new ZMPacket(*packet);
//ZMPacket *audio_packet = new ZMPacket(*packet);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
audio_packet->codec_type = camera->get_AudioStream()->codecpar->codec_type;
packet->codec_type = camera->get_AudioStream()->codecpar->codec_type;
#else
audio_packet->codec_type = camera->get_AudioStream()->codec->codec_type;
packet->codec_type = camera->get_AudioStream()->codec->codec_type;
#endif
Debug(2, "Queueing packet");
packetqueue->queuePacket(audio_packet);
packetqueue->queuePacket(packet);
}
// Don't update last_write_index because that is used for live streaming
//shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
mutex.unlock();
//mutex.unlock();
packet->unlock();
return 1;
}
@ -2274,20 +2275,21 @@ int Monitor::Capture() {
//Debug(2,"About to decode");
if ( packet->decode(camera->get_VideoCodecContext()) ) {
//Debug(2,"Getimage");
packet->get_image();
packet->get_image( image_buffer[index].image );
}
// Have an av_packet,
}
Debug(2,"Before mutex lock");
mutex.lock();
//Debug(2,"Before mutex lock");
// FIXME this mutex is useless, packetqueue has it's own
//mutex.lock();
if ( packetqueue->video_packet_count || packet->keyframe || event ) {
Debug(2, "Have video packet for index (%d)", index);
packetqueue->queuePacket(packet);
} else {
Debug(2, "Not queuing video packet for index (%d)", index);
}
mutex.unlock();
//mutex.unlock();
/* Deinterlacing */
if ( deinterlacing_value ) {
@ -2355,7 +2357,7 @@ int Monitor::Capture() {
// assume that we are connected
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth,Status) "
"VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE "
"VALUES (%d, %.2lf, %u, 'Connected') ON DUPLICATE KEY UPDATE "
"CaptureFPS = %.2lf, CaptureBandwidth=%u, Status='Connected'",
id, capture_fps, new_capture_bandwidth, capture_fps, new_capture_bandwidth);
if ( mysql_query(&dbconn, sql) ) {
@ -2701,24 +2703,18 @@ Monitor::Orientation Monitor::getOrientation() const { return orientation; }
// Wait for camera to get an image, and then assign it as the base reference image. So this should be done as the first task in the analysis thread startup.
void Monitor::get_ref_image() {
while (
( shared_data->last_write_index == (unsigned int)image_buffer_count )
&&
( shared_data->last_write_time == 0 )
&& ! zm_terminate
) {
Info("Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
ZMPacket * snap;
while ( ((!( snap = packetqueue->get_analysis_packet())) || ( snap->image_index == -1 )) && !zm_terminate) {
Debug(1, "Waiting for capture daemon lastwriteindex(%d) lastwritetime(%d)",
shared_data->last_write_index, shared_data->last_write_time);
usleep(10000);
//usleep(10000);
}
if ( zm_terminate )
return;
int last_write_index = shared_data->last_write_index ;
Debug(2,"Waiting for capture daemon unlock");
image_buffer[last_write_index].mutex.lock();
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[last_write_index].image->Buffer(), camera->ImageSize());
image_buffer[last_write_index].mutex.unlock();
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(), snap->image->Buffer(), camera->ImageSize());
Debug(2,"Have image about to unlock");
snap->unlock();
}
std::vector<Group *> Monitor::Groups() {

View File

@ -426,7 +426,7 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
}
}
last_frame_sent = TV_2_FLOAT(now);
return( true );
return true;
} // end bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp )
void MonitorStream::runStream() {

View File

@ -120,6 +120,7 @@ int ZMPacket::decode( AVCodecContext *ctx ) {
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
Debug(4,"send_packet");
int ret = avcodec_send_packet(ctx, &packet);
if ( ret < 0 ) {
Error("Unable to send packet: %s", av_make_error_string(ret).c_str());
@ -143,6 +144,7 @@ int ZMPacket::decode( AVCodecContext *ctx ) {
}
} else {
#endif
Debug(4,"receive_frame");
ret = avcodec_receive_frame(ctx, in_frame);
if ( ret < 0 ) {
Error("Unable to receive frame: %s", av_make_error_string(ret).c_str());

View File

@ -62,8 +62,12 @@ class ZMPacket {
explicit ZMPacket( ZMPacket &packet );
ZMPacket();
~ZMPacket();
void lock() { mutex.lock(); };
void unlock() { mutex.unlock(); };
void lock() {
Debug(2,"Locking packet %d", this->image_index);
mutex.lock();
Debug(2,"packet %d locked", this->image_index);
};
void unlock() { Debug(2,"packet %d unlocked", this->image_index);mutex.unlock(); };
AVFrame *get_out_frame( const AVCodecContext *ctx );
int get_codec_imgsize() { return codec_imgsize; };
};

View File

@ -33,12 +33,15 @@ zm_packetqueue::zm_packetqueue( int video_image_count, int p_video_stream_id, in
packet_counts = new int[max_stream_id+1];
for ( int i=0; i <= max_stream_id; ++i )
packet_counts[i] = 0;
condition = new Condition(mutex);
}
zm_packetqueue::~zm_packetqueue() {
clearQueue();
delete[] packet_counts;
packet_counts = NULL;
delete condition;
condition = NULL;
}
/* Enqueues the given packet. Will maintain the analysis_it pointer and image packet counts.
@ -48,6 +51,7 @@ zm_packetqueue::~zm_packetqueue() {
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
Debug(4, "packetqueue queuepacket, first_video_packet_index is %d", first_video_packet_index);
mutex.lock();
if ( zm_packet->image_index != -1 ) {
// It's a video packet
@ -55,51 +59,33 @@ bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
// If we can never queue the same packet, then they can never go past
if ( zm_packet->image_index == first_video_packet_index ) {
Debug(2, "queuing packet that is already on the queue(%d)", zm_packet->image_index);
ZMPacket *p = NULL;;
while ( pktQueue.size() && (p = pktQueue.front()) && ( p->image_index != zm_packet->image_index ) ) {
if ( ( analysis_it != pktQueue.end() ) && ( *analysis_it == p ) ) {
Debug(2, "Increasing analysis_it, meaning analysis is not keeping up");
++analysis_it;
}
while ( pktQueue.size() ) {
mutex.unlock();
ZMPacket *p = popPacket();
Debug(2,"Front packet index: %d", p->image_index);
mutex.lock();
pktQueue.pop_front();
if ( p->codec_type == AVMEDIA_TYPE_VIDEO ) {
Debug(2, "Decreasing video_packet_count (%d), popped (%d)",
video_packet_count, p->image_index);
video_packet_count -= 1;
first_video_packet_index += 1;
first_video_packet_index %= max_video_packet_count;
} else {
Debug(2, "Deleteing audio frame(%d)", p->image_index);
if ( p->codec_type != AVMEDIA_TYPE_VIDEO ) {
Debug(2, "Deleting audio frame(%d)", p->image_index);
delete p;
p = NULL;
}
Debug(2,"pktQueue.size(%d)", pktQueue.size());
if ( p->image_index == zm_packet->image_index )
break;
} // end while there are packets at the head of the queue that are not this one
if ( p && ( p->image_index == zm_packet->image_index ) ) {
// it should
video_packet_count -= 1;
pktQueue.pop_front();
first_video_packet_index += 1;
first_video_packet_index %= max_video_packet_count;
} else {
Error("SHould have found the packet! front packet index was %d, new packet index is %d ",
p->image_index, zm_packet->image_index
);
}
if ( analysis_it == pktQueue.end() ) {
// Analsys_it should only point to end when queue is empty
Debug(2,"pointing analysis_it to begining");
analysis_it = pktQueue.begin();
}
} else if ( first_video_packet_index == -1 ) {
}
if ( first_video_packet_index == -1 ) {
// Initialize the first_video_packet indicator
first_video_packet_index = zm_packet->image_index;
video_packet_count += 1;
} // end if
video_packet_count += 1;
} // end if queuing a video packet
pktQueue.push_back(zm_packet);
@ -114,6 +100,10 @@ bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
}
#endif
// We signal on every packet because someday we may analyze sound
Debug(2,"Signalling");
condition->signal();
mutex.unlock();
return true;
} // end bool zm_packetqueue::queuePacket(ZMPacket* zm_packet)
@ -122,6 +112,9 @@ ZMPacket* zm_packetqueue::popPacket( ) {
if ( pktQueue.empty() ) {
return NULL;
}
Debug(2,"Mutex locking");
mutex.lock();
Debug(2,"Have Mutex lock");
ZMPacket *packet = pktQueue.front();
if ( *analysis_it == packet )
@ -140,7 +133,9 @@ ZMPacket* zm_packetqueue::popPacket( ) {
}
}
packet_counts[packet->packet.stream_index] -= 1;
mutex.unlock();
// Should we lock the packet?
return packet;
}
@ -155,6 +150,7 @@ unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_
if ( pktQueue.size() <= frames_to_keep ) {
return 0;
}
mutex.lock();
int packets_to_delete = pktQueue.size();
std::list<ZMPacket *>::reverse_iterator it;
@ -241,6 +237,7 @@ unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_
#endif
Debug(3, "Deleted packets, resulting size is %d", pktQueue.size());
mutex.unlock();
return delete_count;
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
@ -278,15 +275,22 @@ int zm_packetqueue::packet_count( int stream_id ) {
// Returns a packet to analyse or NULL
ZMPacket *zm_packetqueue::get_analysis_packet() {
if ( ! pktQueue.size() )
return NULL;
if ( analysis_it == pktQueue.end() )
return NULL;
mutex.lock();
while ( (! pktQueue.size()) || ( analysis_it == pktQueue.end() ) ) {
Debug(2,"waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) );
condition->wait();
}
//Debug(2, "Distance from head: (%d)", std::distance( pktQueue.begin(), analysis_it ) );
//Debug(2, "Distance from end: (%d)", std::distance( analysis_it, pktQueue.end() ) );
ZMPacket *p = *analysis_it;
Debug(2,"analysis_packet image_index: %d, about to lock packet", p->image_index);
p->lock();
Debug(2, "Locked packet, unlocking mutex");
mutex.unlock();
return *analysis_it;
return p;
} // end ZMPacket *zm_packetqueue::get_analysis_packet()
// The idea is that analsys_it will only be == end() if the queue is empty
@ -301,3 +305,44 @@ bool zm_packetqueue::increment_analysis_it( ) {
analysis_it = next_it;
return true;
} // end bool zm_packetqueue::increment_analysis_it( )
// Locks the packet, but also removes it from the queue including analysis_it
void zm_packetqueue::lock_packet( ZMPacket &packet ) {
mutex.lock();
if ( packet.image_index != -1 ) {
// It's a video packet
// If we can never queue the same packet, then they can never go past
if ( packet.image_index == first_video_packet_index ) {
Debug(2, "queuing packet that is already on the queue(%d)", packet.image_index);
while ( pktQueue.size() ) {
mutex.unlock();
ZMPacket *p = popPacket();
Debug(2,"Front packet index: %d", p->image_index);
mutex.lock();
if ( p->codec_type != AVMEDIA_TYPE_VIDEO ) {
Debug(2, "Deleting audio frame(%d)", p->image_index);
delete p;
p = NULL;
}
Debug(2,"pktQueue.size(%d)", pktQueue.size());
if ( p->image_index == packet.image_index )
break;
} // end while there are packets at the head of the queue that are not this one
if ( analysis_it == pktQueue.end() ) {
// Analsys_it should only point to end when queue is empty
Debug(2,"pointing analysis_it to begining");
analysis_it = pktQueue.begin();
}
}
if ( first_video_packet_index == -1 ) {
// Initialize the first_video_packet indicator
first_video_packet_index = packet.image_index;
} // end if
video_packet_count += 1;
} // end if queuing a video packet
packet.lock();
mutex.unlock();
}

View File

@ -42,7 +42,8 @@ class zm_packetqueue {
int max_stream_id;
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
Mutex mutex;
RecursiveMutex mutex;
Condition *condition;
public:
zm_packetqueue(int p_max_video_packet_count, int p_video_stream_id, int p_audio_stream_id);
@ -60,6 +61,7 @@ class zm_packetqueue {
// Functions to manage the analysis frame logic
bool increment_analysis_it();
ZMPacket *get_analysis_packet();
void lock_packet( ZMPacket &packet );
};
#endif /* ZM_PACKETQUEUE_H */

View File

@ -215,8 +215,9 @@ if ( $action ) {
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
Logger::Debug('Redirecting to login');
$view = 'none';
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
# We adjust the view instead of redirecting so that we can store the original url and just to it after logging in
$view = 'login';
#$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
$request = null;
} else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) {
$view = 'none';

View File

@ -172,8 +172,8 @@ if ( canEdit('Events') ) {
} // end if Event->DefaultVideo
?>
<div id="exportEvent"><button type="button" data-on-click="exportEvent"><?php echo translate('Export') ?></button></div>
<div id="replayControl"><label for="replayMode"><?php echo translate('Replay') ?></label><?php echo buildSelect( "replayMode", $replayModes, "changeReplayMode();" ); ?></div>
<div id="scaleControl"><label for="scale"><?php echo translate('Scale') ?></label><?php echo buildSelect( "scale", $scales, "changeScale();" ); ?></div>
<div id="replayControl"><label for="replayMode"><?php echo translate('Replay') ?></label><?php echo buildSelect('replayMode', $replayModes, 'changeReplayMode();'); ?></div>
<div id="scaleControl"><label for="scale"><?php echo translate('Scale') ?></label><?php echo buildSelect('scale', $scales, 'changeScale();'); ?></div>
<div id="codecControl"><label for="codec"><?php echo translate('Codec') ?></label><?php echo htmlSelect('codec', $codecs, $codec, array('onchange'=>'changeCodec(this);') ); ?></div>
</div>
</div>
@ -192,7 +192,6 @@ if ( $video_tag ) {
<?php
} else {
?>
<?php if ( !$video_tag ) { ?>
<div id="imageFeed">
<?php
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
@ -212,7 +211,9 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
<div class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
</div><!--progressBar-->
</div><!--imageFeed-->
<?php } /*end if !DefaultVideo*/ ?>
<?php
} /*end if !DefaultVideo*/
?>
<p id="dvrControls">
<input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" data-on-click-true="streamPrev"/>
<input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" data-on-click-true="streamFastRev"/>

View File

@ -18,8 +18,11 @@
}
?>";
//var newUrl = thisUrl + querySuffix;
var newUrl = '<?php echo $_SERVER['PHP_SELF'] ?>' + querySuffix;
if ( querySuffix == '?view=login' ) {
querySuffix = '?view=console';
}
var newUrl = querySuffix;
console.log("Redirecting to" + newUrl + ' ' + thisUrl);
window.location.replace(newUrl);
}
).delay( 500 );