Merge branch 'master' into rtsp_server

Cleanup Analysis a bit. We can't skip packets just because they are audio. Clean up the state machine transitions a bit to make them a little more readable.
Change logic of PrimeCapture, success MUST return 1.  0 means no error but also no success.
Debugging and braces improvements in local_camera.
This commit is contained in:
Isaac Connor
2020-12-17 10:16:54 -05:00
13 changed files with 190 additions and 174 deletions

View File

@@ -47,10 +47,14 @@ public:
inline const Coord &Lo() const { return lo; }
inline int LoX() const { return lo.X(); }
inline int LoX(int p_lo_x) { return lo.X(p_lo_x); }
inline int LoY() const { return lo.Y(); }
inline int LoY(int p_lo_y) { return lo.Y(p_lo_y); }
inline const Coord &Hi() const { return hi; }
inline int HiX() const { return hi.X(); }
inline int HiX(int p_hi_x) { return hi.X(p_hi_x); }
inline int HiY() const { return hi.Y(); }
inline int HiY(int p_hi_y) { return hi.Y(p_hi_y); }
inline const Coord &Size() const { return size; }
inline int Width() const { return size.X(); }
inline int Height() const { return size.Y(); }

View File

@@ -38,14 +38,14 @@ public:
y = coord.y;
return *this;
}
inline int &X() { return( x ); }
inline const int &X() const { return( x ); }
inline int &Y() { return( y ); }
inline const int &Y() const { return( y ); }
inline int &X(int p_x) { x=p_x; return x; }
inline const int &X() const { return x; }
inline int &Y(int p_y) { y=p_y; return y; }
inline const int &Y() const { return y; }
inline static Coord Range( const Coord &coord1, const Coord &coord2 ) {
Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 );
return( result );
return result;
}
inline bool operator==( const Coord &coord ) const { return( x == coord.x && y == coord.y ); }

View File

@@ -178,6 +178,7 @@ class Event {
return pre_alarm_count;
}
static void EmptyPreAlarmFrames() {
#if 0
while ( pre_alarm_count > 0 ) {
int i = pre_alarm_count - 1;
delete pre_alarm_data[i].image;
@@ -188,6 +189,7 @@ class Event {
}
pre_alarm_count--;
}
#endif
pre_alarm_count = 0;
}
static void AddPreAlarmFrame(
@@ -196,15 +198,18 @@ class Event {
int score=0,
Image *alarm_frame=nullptr
) {
#if 0
pre_alarm_data[pre_alarm_count].image = new Image(*image);
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
pre_alarm_data[pre_alarm_count].score = score;
if ( alarm_frame ) {
pre_alarm_data[pre_alarm_count].alarm_frame = new Image(*alarm_frame);
}
#endif
pre_alarm_count++;
}
void SavePreAlarmFrames() {
#if 0
for ( int i = 0; i < pre_alarm_count; i++ ) {
AddFrame(
pre_alarm_data[i].image,
@@ -212,6 +217,7 @@ class Event {
pre_alarm_data[i].score,
pre_alarm_data[i].alarm_frame);
}
#endif
EmptyPreAlarmFrames();
}
};

View File

@@ -207,7 +207,7 @@ int FfmpegCamera::PrimeCapture() {
mAudioStreamId = -1;
Debug(1, "Priming capture from %s", mPath.c_str());
return ! OpenFfmpeg();
return OpenFfmpeg();
}
int FfmpegCamera::PreCapture() {

View File

@@ -17,26 +17,10 @@ FFmpeg_Input::~FFmpeg_Input() {
if ( input_format_context ) {
Close();
}
if ( streams ) {
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
avcodec_close(streams[i].context);
avcodec_free_context(&streams[i].context);
}
delete[] streams;
streams = nullptr;
}
if ( frame ) {
av_frame_free(&frame);
frame = nullptr;
}
if ( input_format_context ) {
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file(input_format_context);
#else
avformat_close_input(&input_format_context);
#endif
input_format_context = nullptr;
}
} // end ~FFmpeg_Input()
int FFmpeg_Input::Open(
@@ -137,14 +121,16 @@ int FFmpeg_Input::Open(const char *filepath) {
} // end int FFmpeg_Input::Open( const char * filepath )
int FFmpeg_Input::Close( ) {
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
if ( streams[i].context ) {
if ( streams ) {
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
avcodec_close(streams[i].context);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
avcodec_free_context(&streams[i].context);
streams[i].context = nullptr;
#endif
streams[i].context = NULL;
}
delete[] streams;
streams = nullptr;
}
if ( input_format_context ) {
@@ -153,7 +139,7 @@ int FFmpeg_Input::Close( ) {
#else
avformat_close_input(&input_format_context);
#endif
input_format_context = NULL;
input_format_context = nullptr;
}
return 1;
} // end int FFmpeg_Input::Close()
@@ -164,7 +150,6 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
av_init_packet(&packet);
while ( !frameComplete ) {
int ret = av_read_frame(input_format_context, &packet);
if ( ret < 0 ) {
if (
@@ -184,33 +169,33 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id) {
if ( (stream_id >= 0) && (packet.stream_index != stream_id) ) {
Debug(1,"Packet is not for our stream (%d)", packet.stream_index );
return NULL;
}
continue;
}
AVCodecContext *context = streams[packet.stream_index].context;
if ( frame ) {
av_frame_free(&frame);
frame = zm_av_frame_alloc();
if ( frame ) {
av_frame_free(&frame);
frame = zm_av_frame_alloc();
} else {
frame = zm_av_frame_alloc();
}
ret = zm_send_packet_receive_frame(context, frame, packet);
if ( ret < 0 ) {
Error("Unable to decode frame at frame %d: %d %s, continuing",
streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str());
zm_av_packet_unref(&packet);
av_frame_free(&frame);
continue;
} else {
if ( is_video_stream(input_format_context->streams[packet.stream_index]) ) {
zm_dump_video_frame(frame, "resulting video frame");
} else {
frame = zm_av_frame_alloc();
}
ret = zm_send_packet_receive_frame(context, frame, packet);
if ( ret < 0 ) {
Error("Unable to decode frame at frame %d: %d %s, continuing",
streams[packet.stream_index].frame_count, ret, av_make_error_string(ret).c_str());
zm_av_packet_unref(&packet);
av_frame_free(&frame);
continue;
} else {
if ( is_video_stream(input_format_context->streams[packet.stream_index]) ) {
zm_dump_video_frame(frame, "resulting video frame");
} else {
zm_dump_frame(frame, "resulting frame");
}
zm_dump_frame(frame, "resulting frame");
}
}
frameComplete = 1;
frameComplete = 1;
zm_av_packet_unref(&packet);

View File

@@ -447,7 +447,7 @@ LocalCamera::LocalCamera(
#if HAVE_LIBSWSCALE
/* Try using swscale for the conversion */
conversion_type = 1;
Debug(2,"Using swscale for image conversion");
Debug(2, "Using swscale for image conversion");
if ( colours == ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA;
@@ -650,7 +650,7 @@ LocalCamera::LocalCamera(
#if HAVE_LIBSWSCALE
/* Initialize swscale stuff */
if ( capture && conversion_type == 1 ) {
if ( capture and (conversion_type == 1) ) {
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
tmpPicture = av_frame_alloc();
#else
@@ -680,7 +680,7 @@ LocalCamera::LocalCamera(
#endif
mVideoStreamId = 0;
mAudioStreamId = -1;
video_stream = NULL;
video_stream = nullptr;
} // end LocalCamera::LocalCamera
LocalCamera::~LocalCamera() {
@@ -689,7 +689,7 @@ LocalCamera::~LocalCamera() {
#if HAVE_LIBSWSCALE
/* Clean up swscale stuff */
if ( capture && conversion_type == 1 ) {
if ( capture && (conversion_type == 1) ) {
sws_freeContext(imgConversionContext);
imgConversionContext = nullptr;
@@ -699,13 +699,6 @@ LocalCamera::~LocalCamera() {
} // end LocalCamera::~LocalCamera
void LocalCamera::Initialise() {
#if HAVE_LIBSWSCALE
if ( logDebugging() )
av_log_set_level(AV_LOG_DEBUG);
else
av_log_set_level(AV_LOG_QUIET);
#endif // HAVE_LIBSWSCALE
Debug(3, "Opening video device %s", device.c_str());
//if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 )
if ( (vid_fd = open(device.c_str(), O_RDWR, 0)) < 0 )
@@ -2021,12 +2014,12 @@ int LocalCamera::PrimeCapture() {
#endif // ZM_HAS_V4L1
mVideoStreamId = 0;
return 0;
return 1;
} // end LocalCamera::PrimeCapture
int LocalCamera::PreCapture() {
//Debug(5, "Pre-capturing");
return 0;
return 1;
}
int LocalCamera::Capture(ZMPacket &zm_packet) {
@@ -2124,12 +2117,13 @@ int LocalCamera::Capture(ZMPacket &zm_packet) {
} /* prime capture */
if ( ! zm_packet.image ) {
Debug(1, "Allocating image");
zm_packet.image = new Image(width, height, colours, subpixelorder);
}
if ( conversion_type != 0 ) {
Debug(3, "Performing format conversion");
Debug(3, "Performing format conversion %d", conversion_type);
/* Request a writeable buffer of the target image */
uint8_t *directbuffer = zm_packet.image->WriteBuffer(width, height, colours, subpixelorder);

View File

@@ -333,8 +333,8 @@ Monitor::Monitor()
purpose(QUERY),
auto_resume_time(0),
last_motion_score(0),
camera(0),
event(0),
camera(nullptr),
event(nullptr),
n_zones(0),
zones(nullptr),
timestamps(0),
@@ -1768,14 +1768,6 @@ bool Monitor::Analyse() {
packets_processed += 1;
packetqueue->increment_analysis_it();
if ( snap->packet.stream_index != video_stream_id ) {
snap->unlock();
Debug(2, "skipping because audio");
continue;
}
struct timeval *timestamp = snap->timestamp;
Image *snap_image = snap->image;
// signal is set by capture
bool signal = shared_data->signal;
@@ -1820,7 +1812,6 @@ bool Monitor::Analyse() {
signalText = "Reacquired";
score += 100;
}
Warning("%s: %s", SIGNAL_CAUSE, signalText);
if ( event && !signal ) {
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, image_count, event->Id());
closeEvent();
@@ -1835,20 +1826,25 @@ bool Monitor::Analyse() {
noteSetMap[SIGNAL_CAUSE] = noteSet;
shared_data->state = state = IDLE;
shared_data->active = signal;
ref_image.Assign(*snap_image);
ref_image.Assign(*(snap->image));
}// else
if ( signal ) {
if ( snap->packet.stream_index == video_stream_id ) {
struct timeval *timestamp = snap->timestamp;
if ( Active() && (function == MODECT || function == MOCORD) ) {
Debug(3, "signal and active and modect");
Event::StringSet zoneSet;
int motion_score = last_motion_score;
if ( !(analysis_image_count % (motion_frame_skip+1) ) ) {
if ( snap->image ) {
// Get new score.
motion_score = DetectMotion(*snap_image, zoneSet);
motion_score = DetectMotion(*(snap->image), zoneSet);
Debug(3, "After motion detection, last_motion_score(%d), new motion score(%d)", last_motion_score, motion_score);
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score);
} else {
Warning("No image in snap");
}
@@ -1862,7 +1858,6 @@ bool Monitor::Analyse() {
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
//shared_data->active = signal; // unneccessary active gets set on signal change
} // end if active and doing motion detection
// Check to see if linked monitors are triggering.
@@ -2028,45 +2023,69 @@ bool Monitor::Analyse() {
// Back to IDLE
shared_data->state = state = function != MOCORD ? IDLE : TAPE;
} else {
Debug(1, "Not leaving ALERT beacuse image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%d) - recording.tv_src(%d) >= min_section_length(%d)", image_count, last_alarm_count, post_event_count, timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
Debug(1, "State %d ALERT beacuse image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%d) - recording.tv_src(%d) >= min_section_length(%d)",
state, analysis_image_count, last_alarm_count, post_event_count,
timestamp->tv_sec, video_store_data->recording.tv_sec, min_section_length);
}
if ( Event::PreAlarmCount() )
Event::EmptyPreAlarmFrames();
} // end if score or not
// Flag the packet so we don't analyse it again
Debug(1, "Scoring packet");
snap->score = score;
if ( state == PREALARM || state == ALARM ) {
if ( state == PREALARM ) {
// Generate analysis images if necessary
if ( savejpegs > 1 ) {
bool got_anal_image = false;
Image *anal_image = new Image( *snap_image );
//alarm_image.Assign( *snap_image );
for( int i = 0; i < n_zones; i++ ) {
for ( int i = 0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) {
if ( zones[i]->AlarmImage() ) {
anal_image->Overlay( *(zones[i]->AlarmImage()) );
got_anal_image = true;
if ( ! snap->analysis_image )
snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay( *(zones[i]->AlarmImage()) );
}
if ( config.record_event_stats && (state == ALARM) )
} // end if zone is alarmed
} // end foreach zone
} // end if savejpegs
// incremement pre alarm image count
//have_pre_alarmed_frames ++;
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
} else if ( state == ALARM ) {
if ( savejpegs > 1 ) {
for ( int i = 0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) {
if ( zones[i]->AlarmImage() ) {
if ( ! snap->analysis_image )
snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zones[i]->AlarmImage()));
}
if ( config.record_event_stats )
zones[i]->RecordStats(event);
} // end if zone is alarmed
} // end foreach zone
if ( got_anal_image ) {
snap->analysis_image = anal_image;
} else {
delete anal_image;
}
} else if ( config.record_event_stats && state == ALARM ) {
for ( int i = 0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) {
zones[i]->RecordStats(event);
}
} // end foreach zone
} // analsys_images or record stats
}
if ( noteSetMap.size() > 0 )
event->updateNotes( noteSetMap );
event->updateNotes(noteSetMap);
if ( section_length
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
&& ! (image_count % fps_report_interval)
) {
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d",
name, image_count, event->Id(),
timestamp->tv_sec, video_store_data->recording.tv_sec,
timestamp->tv_sec - video_store_data->recording.tv_sec,
section_length
);
closeEvent();
event = new Event(this, *timestamp, cause, noteSetMap);
shared_data->last_event_id = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
}
} else if ( state == ALERT ) {
// Alert means this frame has no motion, but we were alarmed and are still recording.
if ( noteSetMap.size() > 0 )
@@ -2079,11 +2098,14 @@ bool Monitor::Analyse() {
//event->AddFrame( snap_image, *timestamp );
//}
//}
}
if ( function == MODECT || function == MOCORD ) {
ref_image.Blend( *snap_image, ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc ) );
}
last_signal = signal;
} // end if state machine
if ( function == MODECT || function == MOCORD ) {
ref_image.Blend(*(snap->image), ( state==ALARM ? alarm_ref_blend_perc : ref_blend_perc ));
}
last_signal = signal;
} // end if videostream
} // end if signal
} else {
@@ -2826,7 +2848,7 @@ unsigned int Monitor::SubpixelOrder() const { return camera->SubpixelOrder(); }
int Monitor::PrimeCapture() {
int ret = camera->PrimeCapture();
if ( ret == 0 ) {
if ( ret > 0 ) {
if ( packetqueue )
delete packetqueue;
video_stream_id = camera->get_VideoStreamId();
@@ -2835,14 +2857,13 @@ int Monitor::PrimeCapture() {
Debug(2, "Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d",
video_stream_id, audio_stream_id, pre_event_buffer_count);
} else {
Debug(2, "Not Video stream id is %d, audio is %d, minimum_packets to keep in buffer %d",
video_stream_id, audio_stream_id, pre_event_buffer_count);
Debug(2, "Failed to prime %d", ret);
}
return ret;
}
int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture() ; }
int Monitor::PostCapture() const { return camera->PostCapture(); }
int Monitor::Close() {
if ( packetqueue ) {
delete packetqueue;

View File

@@ -100,9 +100,13 @@ public:
inline const Box &Extent() const { return extent; }
inline int LoX() const { return extent.LoX(); }
inline int LoX(int p_lo_x) { return extent.LoX(p_lo_x); }
inline int HiX() const { return extent.HiX(); }
inline int HiX(int p_hi_x) { return extent.HiX(p_hi_x); }
inline int LoY() const { return extent.LoY(); }
inline int LoY(int p_lo_y) { return extent.LoY(p_lo_y); }
inline int HiY() const { return extent.HiY(); }
inline int HiY(int p_hi_y) { return extent.HiY(p_hi_y); }
inline int Width() const { return extent.Width(); }
inline int Height() const { return extent.Height(); }

View File

@@ -269,9 +269,9 @@ void *Thread::mThreadFunc( void *arg ) {
}
void Thread::start() {
Debug( 1, "Starting thread" );
Debug(4, "Starting thread" );
if ( isThread() )
throw ThreadException( "Can't self start thread" );
throw ThreadException("Can't self start thread");
mThreadMutex.lock();
if ( !mStarted ) {
pthread_attr_t threadAttrs;
@@ -287,11 +287,11 @@ void Thread::start() {
}
mThreadCondition.wait();
mThreadMutex.unlock();
Debug( 1, "Started thread %d", mPid );
Debug(4, "Started thread %d", mPid);
}
void Thread::join() {
Debug( 1, "Joining thread %d", mPid );
Debug(1, "Joining thread %d", mPid);
if ( isThread() )
throw ThreadException( "Can't self join thread" );
mThreadMutex.lock();

View File

@@ -148,14 +148,14 @@ bool VideoStore::open() {
int wanted_codec = monitor->OutputCodec();
if ( !wanted_codec ) {
// default to h264
Debug(2, "Defaulting to H264");
wanted_codec = AV_CODEC_ID_H264;
//Debug(2, "Defaulting to H264");
//wanted_codec = AV_CODEC_ID_H264;
} else {
Debug(2, "Codec is %d, wanted %d", video_in_ctx->codec_id, wanted_codec);
}
// FIXME Should check that we are set to passthrough. Might be same codec, but want privacy overlays
if ( video_in_ctx->codec_id == wanted_codec ) {
if ( (!wanted_codec) or (video_in_ctx->codec_id == wanted_codec) ) {
// Copy params from instream to ctx
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar);
@@ -235,6 +235,8 @@ bool VideoStore::open() {
AVDictionary *opts = 0;
std::string Options = monitor->GetEncoderOptions();
Debug(2, "Options?");
Debug(2, "Options? %s", Options.c_str());
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
if ( ret < 0 ) {
Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str());

View File

@@ -891,10 +891,12 @@ int Zone::Load(Monitor *monitor, Zone **&zones) {
if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width()
|| polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) {
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring",
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing",
Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY());
n_zones -= 1;
continue;
if ( polygon.LoX() < 0 ) polygon.LoX(0);
if ( polygon.HiX() >= (int)monitor->Width()) polygon.HiX((int)monitor->Width());
if ( polygon.LoY() < 0 ) polygon.LoY(0);
if ( polygon.HiY() >= (int)monitor->Height() ) polygon.HiY((int)monitor->Height());
}
if ( false && !strcmp( Units, "Percent" ) ) {

View File

@@ -269,15 +269,18 @@ int main(int argc, char *argv[]) {
} // end foreach monitor
// Outer primary loop, handles connection to camera
if ( monitors[0]->PrimeCapture() < 0 ) {
if ( monitors[0]->PrimeCapture() <= 0 ) {
if ( prime_capture_log_count % 60 ) {
Error("Failed to prime capture of initial monitor");
} else {
Debug(1, "Failed to prime capture of initial monitor");
}
prime_capture_log_count ++;
if ( !zm_terminate )
sleep(10);
monitors[0]->disconnect();
if ( !zm_terminate ) {
Debug(1, "Sleeping");
sleep(5);
}
continue;
}
for ( int i = 0; i < n_monitors; i++ ) {
@@ -415,6 +418,7 @@ int main(int argc, char *argv[]) {
delete [] monitors;
Image::Deinitialise();
Debug(1,"terminating");
logTerm();
zmDbClose();

View File

@@ -296,50 +296,44 @@ function getCollapsedNavBarHTML($running, $user, $bandwidth_options, $view, $ski
?>
<div class="fixed-top container-fluid p-0">
<nav class="navbar navbar-dark bg-dark px-1 flex-nowrap">
<nav class="navbar navbar-dark bg-dark px-1 flex-nowrap">
<div class="navbar-brand align-self-start px-0">
<?php echo getNavBrandHTML() ?>
</div>
<nav class="navbar navbar-expand-md align-self-start px-0">
<ul class="nav navbar-nav list-group px-0">
<?php
<div class="navbar-brand align-self-start px-0">
<?php echo getNavBrandHTML() ?>
</div>
<nav class="navbar navbar-expand-md align-self-start px-0">
<?php
// *** Build the statistics shown on the navigation bar ***
if ( (!ZM_OPT_USE_AUTH) or $user ) {
?>
<div id="reload" class="collapse navbar-collapse px-0">
?>
<div id="reload" class="collapse navbar-collapse px-0">
<ul id="Version" class="pr-2">
<?php echo getZMVersionHTML() ?>
</ul>
<ul id="Bandwidth" class="px-2">
<?php echo getBandwidthHTML($bandwidth_options, $user) ?>
</ul>
<?php
echo getSysLoadHTML();
echo getDbConHTML();
echo getStorageHTML();
echo getShmHTML();
echo getLogIconHTML();
?>
</div>
<ul id="Version" class="pr-2">
<?php echo getZMVersionHTML() ?>
</ul>
<ul id="Bandwidth" class="px-2">
<?php echo getBandwidthHTML($bandwidth_options, $user) ?>
</ul>
<?php
echo getSysLoadHTML();
echo getDbConHTML();
echo getStorageHTML();
echo getShmHTML();
echo getLogIconHTML();
?>
</div>
<?php
} // end if (!ZM_OPT_USE_AUTH) or $user )
?>
</nav>
</nav>
<ul class="list-group list-group-horizontal ml-auto">
<ul class="list-group list-group-horizontal ml-auto">
<?php
echo getAcctCircleHTML($skin, $user);
echo getStatusBtnHTML($status);
?>
</ul>
</ul>
<!-- the Navigation Bar Hamburger Button -->
@@ -350,30 +344,30 @@ function getCollapsedNavBarHTML($running, $user, $bandwidth_options, $view, $ski
</button>
<?php } ?>
<div style="background-color:#485460" class="dropdown-menu dropdown-menu-right px-3" id="main-header-nav">
<?php
if ( $user and $user['Username'] ) {
echo '<ul class="navbar-nav">';
echo getConsoleHTML();
echo getOptionsHTML();
echo getLogHTML();
echo getDevicesHTML();
echo getGroupsHTML($view);
echo getFilterHTML($view);
echo getCycleHTML($view);
echo getMontageHTML($view);
echo getMontageReviewHTML($view);
echo getRprtEvntAuditHTML($view);
echo '</ul>';
}
?>
</div>
<div style="background-color:#485460" class="dropdown-menu dropdown-menu-right px-3" id="main-header-nav">
<?php
if ( $user and $user['Username'] ) {
echo '<ul class="navbar-nav">';
echo getConsoleHTML();
echo getOptionsHTML();
echo getLogHTML();
echo getDevicesHTML();
echo getGroupsHTML($view);
echo getFilterHTML($view);
echo getCycleHTML($view);
echo getMontageHTML($view);
echo getMontageReviewHTML($view);
echo getRprtEvntAuditHTML($view);
echo '</ul>';
}
?>
</div>
</nav><!-- End First Navbar -->
</nav><!-- End First Navbar -->
<nav class="navbar navbar-expand-md bg-dark justify-content-center p-0">
<?php echo getConsoleBannerHTML() ?>
</nav><!-- End Second Navbar -->
<nav class="navbar navbar-expand-md bg-dark justify-content-center p-0">
<?php echo getConsoleBannerHTML() ?>
</nav><!-- End Second Navbar -->
</div>
<?php