diff --git a/CMakeLists.txt b/CMakeLists.txt index 04b20247b..cd42d8cfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ if(NOT HOST_OS) endif(NOT HOST_OS) set (CMAKE_CXX_STANDARD 11) -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") # Default CLFAGS and CXXFLAGS: set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 300b44ed6..6b6e0948e 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -482,7 +482,7 @@ CREATE TABLE `Monitors` ( `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' , `VideoWriter` TINYINT NOT NULL DEFAULT '0', `OutputCodec` int(10) unsigned NOT NULL default 0, - `Encoder` enum('auto','h264','h264_omx','mjpeg','mpeg1','mpeg2'), + `Encoder` enum('auto','h264','libx264','h264_omx','h264_vaapi','mjpeg','mpeg1','mpeg2'), `OutputContainer` enum('auto','mp4','mkv'), `EncoderParameters` TEXT, `RecordAudio` TINYINT NOT NULL DEFAULT '0', diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index dd9cb0578..643e08528 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -239,7 +239,7 @@ use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use ZoneMinder::Server qw(CpuLoad); #use Data::Dumper; -use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sending KILL +use constant KILL_DELAY => 10; # seconds to wait between sending TERM and sending KILL our %cmd_hash; our %pid_hash; diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index 504bd456f..d82c74b99 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -156,7 +156,7 @@ while( 1 ) { Error("Error reading shared data for $$monitor{Id} $$monitor{Name}"); } elsif ( !$image_time ) { # We can't get the last capture time so can't be sure it's died. - $restart = 1; + #$restart = 1; Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero."); } else { diff --git a/src/zm_image.h b/src/zm_image.h index 43d2c45ea..73be854ef 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -188,7 +188,7 @@ public: inline unsigned int SubpixelOrder() const { return subpixelorder; } inline unsigned int Size() const { return size; } - inline unsigned int AVPixFormat() { + inline AVPixelFormat AVPixFormat() { if ( colours == ZM_COLOUR_RGB32 ) { return AV_PIX_FMT_RGBA; } else if ( colours == ZM_COLOUR_RGB24 ) { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index b9263cb88..2a5d7f84f 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -999,6 +999,7 @@ bool Monitor::connect() { shared_data->format = camera->SubpixelOrder(); shared_data->imagesize = camera->ImageSize(); shared_data->alarm_cause[0] = 0; + shared_data->last_frame_score = 0; trigger_data->size = sizeof(TriggerData); trigger_data->trigger_state = TRIGGER_CANCEL; trigger_data->trigger_score = 0; @@ -1955,7 +1956,7 @@ bool Monitor::Analyse() { } } noteSet.insert(linked_monitors[i]->Name()); - score += 50; + score += linked_monitors[i]->lastFrameScore(); // 50; } else { Debug(4, "Linked monitor %d %s is not alarmed", linked_monitors[i]->Id(), linked_monitors[i]->Name()); @@ -2195,6 +2196,7 @@ bool Monitor::Analyse() { snap->unlock(); return false; } + shared_data->last_frame_score = score; } else { Debug(3, "trigger == off"); if ( event ) { @@ -2512,7 +2514,7 @@ int Monitor::Capture() { Debug(1, "Not decoding"); } else { Debug(2,"About to decode %p", packet); - if ( ! packet->decode(camera->get_VideoCodecContext()) ) { + if ( packet->decode(camera->get_VideoCodecContext()) < 0 ) { Error("decode failed"); } // end if decode } // end if decoding diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 806fd7cf3..f87cdcd8f 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -135,7 +135,8 @@ protected: uint8_t signal; /* +54 */ uint8_t format; /* +55 */ uint32_t imagesize; /* +56 */ - uint32_t epadding1; /* +60 */ + uint32_t last_frame_score; /* +60 */ + // uint32_t epadding1; /* +60 */ /* ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. @@ -228,6 +229,10 @@ protected: inline bool isConnected() const { return connected && shared_data->valid; } inline time_t getLastConnectTime() const { return last_connect_time; } + inline uint32_t lastFrameScore() { + return shared_data->last_frame_score; + } + bool connect(); bool disconnect(); diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 8bf346a07..e6984126d 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -216,11 +216,12 @@ AVFrame *ZMPacket::get_out_frame( const AVCodecContext *ctx ) { Error("Unable to allocate a frame"); return nullptr; } + #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) codec_imgsize = av_image_get_buffer_size( ctx->pix_fmt, ctx->width, - ctx->height, 1); + ctx->height, 32); buffer = (uint8_t *)av_malloc(codec_imgsize); av_image_fill_arrays( out_frame->data, @@ -229,7 +230,7 @@ AVFrame *ZMPacket::get_out_frame( const AVCodecContext *ctx ) { ctx->pix_fmt, ctx->width, ctx->height, - 1); + 32); #else codec_imgsize = avpicture_get_size( ctx->pix_fmt, diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 69d4eae23..7c6df3374 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -28,11 +28,10 @@ zm_packetqueue::zm_packetqueue( int p_video_stream_id, int p_audio_stream_id ): -video_stream_id(p_video_stream_id), + video_stream_id(p_video_stream_id), + max_video_packet_count(video_image_count), deleting(false) { - video_stream_id = p_video_stream_id; - max_video_packet_count = video_image_count-1; analysis_it = pktQueue.begin(); max_stream_id = p_video_stream_id > p_audio_stream_id ? p_video_stream_id : p_audio_stream_id; @@ -43,7 +42,6 @@ video_stream_id(p_video_stream_id), zm_packetqueue::~zm_packetqueue() { deleting = true; - Debug(4, "In destructor"); /* zma might be waiting. Must have exclusive access */ while ( ! mutex.try_lock() ) { Debug(4, "Waiting for exclusive access"); @@ -51,12 +49,9 @@ zm_packetqueue::~zm_packetqueue() { } while ( !pktQueue.empty() ) { - Debug(4, "Fronting packet %d", pktQueue.empty()); ZMPacket * packet = pktQueue.front(); - Debug(4, "poppng packet %d", packet->image_index); pktQueue.pop_front(); if ( packet->image_index == -1 ) { - Debug(4, "Deletng packet"); delete packet; } } @@ -79,6 +74,8 @@ bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) { pktQueue.push_back(zm_packet); packet_counts[zm_packet->packet.stream_index] += 1; + Debug(1, "packet counts for %d is %d", + zm_packet->packet.stream_index, packet_counts[zm_packet->packet.stream_index]); if ( analysis_it == pktQueue.end() ) { // Analsys_it should only point to end when queue is empty Debug(4, "pointing analysis_it to back"); @@ -95,8 +92,8 @@ bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) { while ( packet_counts[video_stream_id] > max_video_packet_count ) { //clearQueue(max_video_packet_count, video_stream_id); //clearQueue is rather heavy. Since this is the only packet injection spot, we can just start at the beginning of the queue and remove packets until we get to the next video keyframe - Debug(1, "Deleting a packet with stream index (%d) with keyframe(%d), Image_index(%d) video_frames_to_keep is (%d) max: %d", - zm_packet->packet.stream_index, zm_packet->keyframe, zm_packet->image_index, packet_counts[video_stream_id] , max_video_packet_count); + Debug(1, "Deleting a packet with stream index (%d) with keyframe(%d), video_frames_to_keep is (%d) max: %d", + zm_packet->packet.stream_index, zm_packet->keyframe, packet_counts[video_stream_id], max_video_packet_count); ZMPacket *zm_packet = *pktQueue.begin(); pktQueue.pop_front(); packet_counts[zm_packet->packet.stream_index] -= 1; diff --git a/src/zm_swscale.cpp b/src/zm_swscale.cpp index c6ae02936..a47e0de35 100644 --- a/src/zm_swscale.cpp +++ b/src/zm_swscale.cpp @@ -25,8 +25,7 @@ #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL SWScale::SWScale() : gotdefaults(false), swscale_ctx(nullptr), input_avframe(nullptr), output_avframe(nullptr) { - Debug(4,"SWScale object created"); - + Debug(4, "SWScale object created"); } bool SWScale::init() { @@ -68,10 +67,14 @@ SWScale::~SWScale() { swscale_ctx = nullptr; } - Debug(4,"SWScale object destroyed"); + Debug(4, "SWScale object destroyed"); } -int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { +int SWScale::SetDefaults( + enum _AVPIXELFORMAT in_pf, + enum _AVPIXELFORMAT out_pf, + unsigned int width, + unsigned int height) { /* Assign the defaults */ default_input_pf = in_pf; @@ -109,16 +112,16 @@ int SWScale::Convert( break; } /* Get the context */ - swscale_ctx = sws_getCachedContext( swscale_ctx, + swscale_ctx = sws_getCachedContext(swscale_ctx, in_frame->width, in_frame->height, format, out_frame->width, out_frame->height, (AVPixelFormat)out_frame->format, - SWS_FAST_BILINEAR, NULL, NULL, NULL ); + SWS_FAST_BILINEAR, NULL, NULL, NULL); if ( swscale_ctx == NULL ) { Error("Failed getting swscale context"); return -6; } /* Do the conversion */ - if(!sws_scale(swscale_ctx, in_frame->data, in_frame->linesize, 0, in_frame->height, out_frame->data, out_frame->linesize ) ) { + if ( !sws_scale(swscale_ctx, in_frame->data, in_frame->linesize, 0, in_frame->height, out_frame->data, out_frame->linesize ) ) { Error("swscale conversion failed"); return -10; } @@ -138,6 +141,8 @@ int SWScale::Convert( unsigned int new_width, unsigned int new_height ) { + Debug(1, "Convert: in_buffer %p in_buffer_size %d out_buffer %p size %d width %d height %d width %d height %d", + in_buffer, in_buffer_size, out_buffer, out_buffer_size, width, height, new_width, new_height); /* Parameter checking */ if ( in_buffer == nullptr ) { Error("NULL Input buffer"); @@ -151,7 +156,7 @@ int SWScale::Convert( // Error("Invalid input or output pixel formats"); // return -2; // } - if (!width || !height || !new_height || !new_width) { + if ( !width || !height || !new_height || !new_width ) { Error("Invalid width or height"); return -3; } @@ -188,7 +193,7 @@ int SWScale::Convert( /* Check the buffer sizes */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t insize = av_image_get_buffer_size(in_pf, width, height, 1); + size_t insize = av_image_get_buffer_size(in_pf, width, height, 32); #else size_t insize = avpicture_get_size(in_pf, width, height); #endif @@ -197,7 +202,7 @@ int SWScale::Convert( return -4; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t outsize = av_image_get_buffer_size(out_pf, new_width, new_height, 1); + size_t outsize = av_image_get_buffer_size(out_pf, new_width, new_height, 32); #else size_t outsize = avpicture_get_size(out_pf, new_width, new_height); #endif @@ -208,7 +213,9 @@ int SWScale::Convert( } /* Get the context */ - swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, new_width, new_height, out_pf, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr ); + swscale_ctx = sws_getCachedContext( + swscale_ctx, width, height, in_pf, new_width, new_height, + out_pf, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); if ( swscale_ctx == nullptr ) { Error("Failed getting swscale context"); return -6; @@ -216,7 +223,7 @@ int SWScale::Convert( /* Fill in the buffers */ #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize, + if ( av_image_fill_arrays(input_avframe->data, input_avframe->linesize, (uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) { #else if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer, @@ -226,10 +233,10 @@ int SWScale::Convert( return -7; } #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, + if ( av_image_fill_arrays(output_avframe->data, output_avframe->linesize, out_buffer, out_pf, new_width, new_height, 1) <= 0) { #else - if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width, + if ( avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, new_width, new_height) <= 0) { #endif Error("Failed filling output frame with output buffer"); @@ -237,7 +244,9 @@ int SWScale::Convert( } /* Do the conversion */ - if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { + if ( !sws_scale(swscale_ctx, + input_avframe->data, input_avframe->linesize, + 0, height, output_avframe->data, output_avframe->linesize ) ) { Error("swscale conversion failed"); return -10; } @@ -245,18 +254,33 @@ int SWScale::Convert( return 0; } -int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { +int SWScale::Convert( + const uint8_t* in_buffer, + const size_t in_buffer_size, + uint8_t* out_buffer, + const size_t out_buffer_size, + enum _AVPIXELFORMAT in_pf, + enum _AVPIXELFORMAT out_pf, + unsigned int width, + unsigned int height) { return Convert(in_buffer, in_buffer_size, out_buffer, out_buffer_size, in_pf, out_pf, width, height, width, height); } -int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { +int SWScale::Convert( + const Image* img, + uint8_t* out_buffer, + const size_t out_buffer_size, + enum _AVPIXELFORMAT in_pf, + enum _AVPIXELFORMAT out_pf, + unsigned int width, + unsigned int height) { if ( img->Width() != width ) { - Error("Source image width differs. Source: %d Output: %d",img->Width(), width); + Error("Source image width differs. Source: %d Output: %d", img->Width(), width); return -12; } if ( img->Height() != height ) { - Error("Source image height differs. Source: %d Output: %d",img->Height(), height); + Error("Source image height differs. Source: %d Output: %d", img->Height(), height); return -13; } diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index cd450ac24..b6f17b17b 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -88,10 +88,12 @@ VideoStore::VideoStore( audio_next_pts = 0; out_format = NULL; oc = NULL; + + swscale.init(); } // VideoStore::VideoStore bool VideoStore::open() { - Info("Opening video storage stream %s format: %s", filename, format); + Debug(1, "Opening video storage stream %s format: %s", filename, format); int ret = avformat_alloc_output_context2(&oc, nullptr, nullptr, filename); if ( ret < 0 ) { @@ -132,6 +134,19 @@ bool VideoStore::open() { zm_dump_codecpar(video_in_stream->codecpar); } + int wanted_codec = monitor->OutputCodec(); + if ( !wanted_codec ) { + // default to h264 + Debug(2, "Defaulting to H264"); + wanted_codec = AV_CODEC_ID_H264; + // FIXME what is the optimal codec? Probably low latency h264 which is effectively mjpeg + } else { + Debug(2, "Codec is %d, wanted %d", video_in_ctx->codec_id, wanted_codec); + } + std::string wanted_encoder = monitor->Encoder(); + + // FIXME Should check that we are set to passthrough. Might be same codec, but want privacy overlays + if ( (!wanted_codec) or (video_in_ctx->codec_id == wanted_codec) ) { video_out_ctx = avcodec_alloc_context3(NULL); if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) @@ -140,23 +155,19 @@ bool VideoStore::open() { video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; #endif } + if ( !video_out_ctx->codec_tag ) { video_out_ctx->codec_tag = - av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id); + av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id); Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag); } - int wanted_codec = monitor->OutputCodec(); - if ( !wanted_codec ) { - // default to 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 ( (!wanted_codec) or (video_in_ctx->codec_id == wanted_codec) ) { - // Copy params from instream to ctx + video_out_ctx->time_base = video_in_ctx->time_base; + if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + Debug(2,"No timebase found in video in context, defaulting to Q"); + video_out_ctx->time_base = AV_TIME_BASE_Q; + } + // Copy params from instream to ctx + // There might not be a useful video_in_stream. v4l in might not populate this very #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); #else @@ -167,7 +178,6 @@ bool VideoStore::open() { return false; } //video_out_ctx->time_base = (AVRational){1, 1000000}; // microseconds as base frame rate - video_out_ctx->time_base = video_in_ctx->time_base; // Fix deprecated formats switch ( video_out_ctx->pix_fmt ) { case AV_PIX_FMT_YUVJ422P : @@ -187,18 +197,44 @@ bool VideoStore::open() { } } else { // Either no video in or not the desired codec for ( unsigned int i = 0; i < sizeof(codec_data) / sizeof(*codec_data); i++ ) { + if ( wanted_encoder != "" ) { + if ( wanted_encoder != codec_data[i].codec_name ) { + Debug(1, "Not the right codec name %s != %s", codec_data[i].codec_name, wanted_encoder.c_str()); + continue; + } + } if ( codec_data[i].codec_id != wanted_codec ) { Debug(1, "Not the right codec %d != %d", codec_data[i].codec_id, wanted_codec); continue; } video_out_codec = avcodec_find_encoder_by_name(codec_data[i].codec_name); - if ( ! video_out_codec ) { + if ( !video_out_codec ) { Debug(1, "Didn't find encoder for %s", codec_data[i].codec_name); continue; } Debug(1, "Found video codec for %s", codec_data[i].codec_name); + video_out_ctx = avcodec_alloc_context3(video_out_codec); + if ( oc->oformat->flags & AVFMT_GLOBALHEADER ) { +#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) + video_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else + video_out_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif + } + if ( !video_out_ctx->codec_tag ) { + video_out_ctx->codec_tag = + av_codec_get_tag(oc->oformat->codec_tag, video_out_ctx->codec_id); + Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag); + } + video_out_ctx->time_base = video_in_ctx->time_base; + if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + Debug(2,"No timebase found in video in context, defaulting to Q"); + video_out_ctx->time_base = AV_TIME_BASE_Q; + } + + video_out_ctx->codec_id = codec_data[i].codec_id; video_out_ctx->pix_fmt = codec_data[i].pix_fmt; video_out_ctx->level = 32; @@ -207,22 +243,22 @@ bool VideoStore::open() { video_out_ctx->height = monitor->Height(); video_out_ctx->codec_type = AVMEDIA_TYPE_VIDEO; - // Just copy them from the in, no reason to choose different - video_out_ctx->time_base = video_in_ctx->time_base; - if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { - Debug(2,"No timebase found in video in context, defaulting to Q"); - video_out_ctx->time_base = AV_TIME_BASE_Q; - } - video_out_stream->time_base = video_in_stream ? video_in_stream->time_base : AV_TIME_BASE_Q; - if ( video_out_ctx->codec_id == AV_CODEC_ID_H264 ) { + /* + video_out_ctx->bit_rate = 2000000; + video_out_ctx->gop_size = 12; video_out_ctx->max_b_frames = 1; if ( video_out_ctx->priv_data ) { - //av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); - //av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); + Debug(2, "setting priv_data crf"); + av_opt_set(video_out_ctx->priv_data, "crf", "1", AV_OPT_SEARCH_CHILDREN); + Debug(2, "setting priv_data preset"); + av_opt_set(video_out_ctx->priv_data, "preset", "ultrafast", 0); } else { Debug(2, "Not setting priv_data"); + av_opt_set(video_out_ctx->priv_data, "preset", "fast", 0); + Debug(2, "Not setting priv_data"); } + */ } else if ( video_out_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO ) { /* just for testing, we also add B frames */ video_out_ctx->max_b_frames = 2; @@ -235,13 +271,12 @@ 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()); } else { - AVDictionaryEntry *e = NULL; + AVDictionaryEntry *e = nullptr; while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { Debug(3, "Encoder Option %s=%s", e->key, e->value); } @@ -252,16 +287,15 @@ bool VideoStore::open() { video_out_codec->name, av_make_error_string(ret).c_str() ); - video_out_codec = NULL; + video_out_codec = nullptr; } - AVDictionaryEntry *e = NULL; - while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { + AVDictionaryEntry *e = nullptr; + while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != nullptr ) { Warning("Encoder Option %s not recognized by ffmpeg codec", e->key); } av_dict_free(&opts); if ( video_out_codec ) break; - } // end foreach codec if ( !video_out_codec ) { @@ -270,12 +304,12 @@ bool VideoStore::open() { // We allocate and copy in newer ffmpeg, so need to free it avcodec_free_context(&video_out_ctx); #endif - video_out_ctx = NULL; + video_out_ctx = nullptr; return false; } // end if can't open codec - Debug(2, "Sucess opening codec"); + Debug(2, "Success opening codec"); } // end if copying or transcoding } // end if video_in_stream @@ -1026,11 +1060,11 @@ bool VideoStore::setup_resampler() { #endif } // end bool VideoStore::setup_resampler() -int VideoStore::writePacket( ZMPacket *ipkt ) { +int VideoStore::writePacket(ZMPacket *ipkt) { if ( ipkt->packet.stream_index == video_in_stream_index ) { - return writeVideoFramePacket( ipkt ); + return writeVideoFramePacket(ipkt); } else if ( ipkt->packet.stream_index == audio_in_stream_index ) { - return writeAudioFramePacket( ipkt ); + return writeAudioFramePacket(ipkt); } Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)", ipkt->packet.stream_index, video_in_stream_index, ( audio_in_stream ? audio_in_stream_index : -1 ) @@ -1044,10 +1078,10 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { // if we have to transcode if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) { - //Debug(3, "Have encoding video frame count (%d)", frame_count); + Debug(3, "Have encoding video frame count (%d)", frame_count); if ( !zm_packet->out_frame ) { - //Debug(3, "Have no out frame"); + Debug(3, "Have no out frame"); AVFrame *out_frame = zm_packet->get_out_frame(video_out_ctx); if ( !out_frame ) { Error("Unable to allocate a frame"); @@ -1055,23 +1089,23 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { } if ( !zm_packet->in_frame ) { - //Debug(2,"Have no in_frame"); + Debug(2, "Have no in_frame"); if ( zm_packet->packet.size ) { - //Debug(2,"Decoding"); + Debug(2, "Decoding"); if ( !zm_packet->decode(video_in_ctx) ) { Debug(2, "unable to decode yet."); return 0; } - //Go straight to out frame + // Go straight to out frame swscale.Convert(zm_packet->in_frame, out_frame); } else if ( zm_packet->image ) { - //Debug(2,"Have an image, convert it"); + Debug(2, "Have an image, convert it"); //Go straight to out frame swscale.Convert( zm_packet->image, zm_packet->buffer, zm_packet->codec_imgsize, - (AVPixelFormat)zm_packet->image->AVPixFormat(), + zm_packet->image->AVPixFormat(), video_out_ctx->pix_fmt, video_out_ctx->width, video_out_ctx->height @@ -1120,7 +1154,7 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { } av_init_packet(&opkt); - opkt.data = NULL; + opkt.data = nullptr; opkt.size = 0; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Do this to allow the encoder to choose whether to use I/P/B frame @@ -1168,37 +1202,41 @@ int VideoStore::writeVideoFramePacket(ZMPacket *zm_packet) { opkt.dts = av_rescale_q(opkt.dts, video_out_ctx->time_base, video_out_stream->time_base); int64_t duration; - if ( zm_packet->in_frame->pkt_duration ) { - duration = av_rescale_q( - zm_packet->in_frame->pkt_duration, - video_in_stream->time_base, - video_out_stream->time_base); - Debug(1, "duration from ipkt: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ") (%d/%d) (%d/%d)", - zm_packet->in_frame->pts, - video_last_pts, - zm_packet->in_frame->pkt_duration, - duration, - video_in_stream->time_base.num, - video_in_stream->time_base.den, - video_out_stream->time_base.num, - video_out_stream->time_base.den - ); - } else { - duration = - av_rescale_q( - zm_packet->in_frame->pts - video_last_pts, + if ( zm_packet->in_frame ) { + if ( zm_packet->in_frame->pkt_duration ) { + duration = av_rescale_q( + zm_packet->in_frame->pkt_duration, video_in_stream->time_base, video_out_stream->time_base); - Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ")", - zm_packet->in_frame->pts, - video_last_pts, - zm_packet->in_frame->pts - video_last_pts, - duration - ); - if ( duration <= 0 ) { - duration = zm_packet->in_frame->pkt_duration ? zm_packet->in_frame->pkt_duration : av_rescale_q(1,video_in_stream->time_base, video_out_stream->time_base); - } - } + Debug(1, "duration from ipkt: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ") (%d/%d) (%d/%d)", + zm_packet->in_frame->pts, + video_last_pts, + zm_packet->in_frame->pkt_duration, + duration, + video_in_stream->time_base.num, + video_in_stream->time_base.den, + video_out_stream->time_base.num, + video_out_stream->time_base.den + ); + } else { + duration = + av_rescale_q( + zm_packet->in_frame->pts - video_last_pts, + video_in_stream->time_base, + video_out_stream->time_base); + Debug(1, "duration calc: pts(%" PRId64 ") - last_pts(%" PRId64 ") = (%" PRId64 ") => (%" PRId64 ")", + zm_packet->in_frame->pts, + video_last_pts, + zm_packet->in_frame->pts - video_last_pts, + duration + ); + if ( duration <= 0 ) { + duration = zm_packet->in_frame->pkt_duration ? zm_packet->in_frame->pkt_duration : av_rescale_q(1,video_in_stream->time_base, video_out_stream->time_base); + } + } // end if in_frmae->pkt_duration + } else { + duration = av_rescale_q(1,video_in_stream->time_base, video_out_stream->time_base); + } // end if in_frmae opkt.duration = duration; } else { // codec matches, we are doing passthrough diff --git a/src/zm_videostore.h b/src/zm_videostore.h index e6ea83ab9..eb9cf486a 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -24,7 +24,7 @@ class VideoStore { private: struct CodecData { - const int codec_id; + const AVCodecID codec_id; const char *codec_codec; const char *codec_name; const enum AVPixelFormat pix_fmt; diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index fb3b70beb..6eff83cc2 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -156,32 +156,21 @@ class MonitorsController extends AppController { return; } + $monitor = $this->Monitor->find('first', array( + 'conditions' => array('Id' => $id) + ))['Monitor']; + $message = ''; if ( $this->Monitor->save($this->request->data) ) { $message = 'Saved'; - $Monitor = $this->Monitor->find('first', array( - 'fields' => array('Function','ServerId'), + + // Stop the monitor. Should happen before saving + $this->Monitor->daemonControl($monitor, 'stop'); + $monitor = $this->Monitor->find('first', array( 'conditions' => array('Id' => $id) ))['Monitor']; - // - restart or stop this monitor after change - $func = $Monitor['Function']; - // We don't pass the request data as the monitor object because it may be a subset of the full monitor array - $this->daemonControl($this->Monitor->id, 'stop'); - if ( - ( $func != 'None' ) - and - ( - (!defined('ZM_SERVER_ID')) - or - ($Monitor['ServerId']==ZM_SERVER_ID) - ) - ) { - if ( !defined('ZM_SERVER_ID')) { - ZM\Debug("Not defined ZM_SERVER_ID"); - } - $this->daemonControl($this->Monitor->id, 'start'); - } + $this->Monitor->daemonControl($monitor, 'start'); } else { $message = 'Error ' . print_r($this->Monitor->invalidFields(), true); } @@ -353,7 +342,6 @@ class MonitorsController extends AppController { } public function daemonControl($id, $command, $daemon=null) { - // Need to see if it is local or remote $monitor = $this->Monitor->find('first', array( 'fields' => array('Type', 'Function', 'Device'), @@ -361,35 +349,8 @@ class MonitorsController extends AppController { )); $monitor = $monitor['Monitor']; - $daemons = array(); - if ( ! $daemon ) { - if ( $monitor['Function'] == 'Monitor' ) { - array_push($daemons, 'zmc'); - } else { - array_push($daemons, 'zmc', 'zma'); - } - } else { - array_push($daemons, $daemon); - } - - $zm_path_bin = Configure::read('ZM_PATH_BIN'); + $status_text = $this->Monitor->daemonControl($monitor, $command, $daemon); - $status_text = ''; - foreach ( $daemons as $daemon ) { - $args = ''; - if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) { - $args = '-d ' . $monitor['Device']; - } else if ( $daemon == 'zmcontrol.pl' ) { - $args = '--id '.$id; - } else { - $args = '-m ' . $id; - } - - $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args"); - ZM\Debug("Command $shellcmd"); - $status = exec($shellcmd); - $status_text .= $status."\n"; - } $this->set(array( 'status' => 'ok', 'statustext' => $status_text, diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 2de0b6fcb..8a95e92d0 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -136,4 +136,44 @@ class Monitor extends AppModel { 'joinTable' => 'Monitor_Status', ) ); + + public function daemonControl($monitor, $command, $daemon=null) { + if ( $monitor['Function'] == 'None' ) { + ZM\Debug('Calling daemonControl when Function == None'); + return; + } + if ( defined('ZM_SERVER_ID') and ($monitor['ServerId']!=ZM_SERVER_ID) ) { + ZM\Debug('Calling daemonControl for Monitor assigned to different server'); + return; + } + + $daemons = array(); + if ( ! $daemon ) { + if ( $monitor['Function'] == 'Monitor' ) { + array_push($daemons, 'zmc'); + } else { + array_push($daemons, 'zmc', 'zma'); + } + } else { + array_push($daemons, $daemon); + } + + $status_text = ''; + foreach ( $daemons as $daemon ) { + $args = ''; + if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) { + $args = '-d ' . $monitor['Device']; + } else if ( $daemon == 'zmcontrol.pl' ) { + $args = '--id '.$monitor['Id']; + } else { + $args = '-m ' . $monitor['Id']; + } + + $shellcmd = escapeshellcmd(ZM_PATH_BIN.'/zmdc.pl '.$command.' '.$daemon.' '.$args); + ZM\Debug("Command $shellcmd"); + $status = exec($shellcmd); + $status_text .= $status.PHP_EOL; + } # end foreach daemon + return $status_text; + } # end function daemonControl } diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php index 80f9083af..968a7be85 100644 --- a/web/includes/actions/monitor.php +++ b/web/includes/actions/monitor.php @@ -202,6 +202,9 @@ if ( $action == 'save' ) { } // end foreach zone } // end if rotation or just size change } // end if changes in width or height + } else { + global $error_message; + $error_message = dbError(); } // end if successful save $restart = true; } else { // new monitor diff --git a/web/includes/database.php b/web/includes/database.php index 889d0ab16..34ea384fa 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -112,10 +112,10 @@ function dbLog($sql, $update=false) { function dbError($sql) { global $dbConn; $error = $dbConn->errorInfo(); - if ( ! $error[0] ) + if ( !$error[0] ) return ''; - $message = "SQL-ERR '".implode("\n",$dbConn->errorInfo())."', statement was '".$sql."'"; + $message = "SQL-ERR '".implode("\n", $dbConn->errorInfo())."', statement was '".$sql."'"; ZM\Error($message); return $message; } diff --git a/web/js/logger.js b/web/js/logger.js index 20e52b726..56638b946 100644 --- a/web/js/logger.js +++ b/web/js/logger.js @@ -16,6 +16,8 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // +$j.ajaxSetup({timeout: AJAX_TIMEOUT}); +var reportLogs = true; if ( !window.console ) { window.console = @@ -28,55 +30,63 @@ if ( !window.console ) { error: function() {} }; } + if ( !console.debug ) { // IE8 has console but doesn't have console.debug so lets alias it. console.debug = console.log; } -var reportLogs = true; +window.onerror = function(message, url, line) { + logReport("ERR", message, url, line); +}; -var debugParms; -var debugReq; +window.addEventListener("securitypolicyviolation", function logCSP(evt) { + var level = evt.disposition == "enforce" ? "ERR" : "DBG"; + var message = evt.blockedURI + " violated CSP " + evt.violatedDirective; + + if ( evt.sample ) message += " (Sample: " + evt.sample + ")"; + logReport(level, message, evt.sourceFile, evt.lineNumber); +}); function logReport( level, message, file, line ) { - if ( !reportLogs ) { - return; - } + if ( !reportLogs ) return; - if ( typeof(MooTools) == "undefined" ) { - return; - } /* eslint-disable no-caller */ if ( arguments && arguments.callee && arguments.callee.caller && arguments.callee.caller.caller && arguments.callee.caller.caller.name ) { message += ' - '+arguments.callee.caller.caller.name+'()'; //console.log("arguments"); - } else { - //message += new Error().stack; - //console.log("stack"); } - /* eslint-enable no-caller */ - if ( !debugReq ) { - debugParms = "view=request&request=log&task=create"; - if ( Browser ) { - debugParms += "&browser[name]="+Browser.name+"&browser[version]="+Browser.version+"&browser[platform]="+(Browser.Platform?Browser.Platform.name:'unknown'); - } else { - debugParms += "&browser[name]=unknown&browser[version]=unknown&browser[platform]=unknown"; - } - debugReq = new Request.JSON({url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain'}); + var browser = {}; + if ( Browser ) { + browser.name = Browser.name; + browser.version = Browser.version; + browser.platform = Browser.Platform ? Browser.Platform.name : 'unknown'; + } else { + browser.name = 'unknown'; + browser.version = 'unknown'; + browser.platform = 'unknown'; } - var requestParms = debugParms; - requestParms += "&level="+level+"&message="+encodeURIComponent(message); + + var data = { + view: 'request', + request: 'log', + task: 'create', + level: level, + message: encodeURIComponent(message), + browser: browser + }; + if ( file ) { - requestParms += "&file="+file; + data.file = file; } else if ( location.search ) { //location.search is the querystring part, so ?blah=blah but there is almost never any value to this - requestParms += "&file="+location.search; + data.file = location.search; } - if ( line ) { - requestParms += "&line="+line; - } - debugReq.send(requestParms); + + if ( line ) data.line = line; + + $j.getJSON(thisUrl, data); } function Panic(message) { @@ -112,22 +122,6 @@ function Debug(message) { } function Dump(value, label) { - if ( label ) { - console.debug(label+" => "); - } + if ( label ) console.debug(label+" => "); console.debug(value); } - -window.onerror = - function( message, url, line ) { - logReport("ERR", message, url, line); - }; - -window.addEventListener("securitypolicyviolation", function logCSP(evt) { - var level = evt.disposition == "enforce" ? "ERR" : "DBG"; - var message = evt.blockedURI + " violated CSP " + evt.violatedDirective; - if ( evt.sample ) { - message += " (Sample: " + evt.sample + ")"; - } - logReport(level, message, evt.sourceFile, evt.lineNumber); -}); diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index 925018a12..6a333c47c 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -437,9 +437,9 @@ function secsToTime( seconds ) { function submitTab(evt) { var tab = this.getAttribute("data-tab-name"); - var form = $('contentForm'); - form.action.value = ""; - form.tab.value = tab; + var form = $j('#contentForm'); + form.attr('action', ''); + form.attr('tab', tab); form.submit(); evt.preventDefault(); } @@ -599,11 +599,11 @@ function scaleToFit(baseWidth, baseHeight, scaleEl, bottomEl) { return {width: Math.floor(newWidth), height: Math.floor(newHeight), autoScale: autoScale}; } -function setButtonState(element_id, butClass) { - var element = $(element_id); +function setButtonState(element_id, btnClass) { + var element = document.getElementById(element_id); if ( element ) { - element.className = butClass; - if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { + element.className = btnClass; + if (btnClass == 'unavail' || (btnClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) { element.disabled = true; } else { element.disabled = false; diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php index 73941b1bb..14b6fac9c 100644 --- a/web/skins/classic/views/event.php +++ b/web/skins/classic/views/event.php @@ -156,6 +156,7 @@ if ( !$Event->Id() ) { } ?> + diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 92a07fecc..52cb5c3f0 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -102,7 +102,7 @@ getBodyTopHTML();