diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index f8fb8673b..d1952f66a 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -351,6 +351,7 @@ CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups_Monitors` (`MonitorId`); DROP TABLE IF EXISTS `Logs`; CREATE TABLE `Logs` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `TimeKey` decimal(16,6) NOT NULL, `Component` varchar(32) NOT NULL, `ServerId` int(10) unsigned, @@ -360,6 +361,7 @@ CREATE TABLE `Logs` ( `Message` text NOT NULL, `File` varchar(255) DEFAULT NULL, `Line` smallint(5) unsigned DEFAULT NULL, + PRIMARY KEY (`Id`), KEY `TimeKey` (`TimeKey`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -589,6 +591,7 @@ CREATE INDEX `Servers_Name_idx` ON `Servers` (`Name`); DROP TABLE IF EXISTS `Stats`; CREATE TABLE `Stats` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `MonitorId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0', `EventId` BIGINT UNSIGNED NOT NULL, @@ -605,6 +608,7 @@ CREATE TABLE `Stats` ( `MinY` smallint(5) unsigned NOT NULL default '0', `MaxY` smallint(5) unsigned NOT NULL default '0', `Score` smallint(5) unsigned NOT NULL default '0', + PRIMARY KEY (`Id`), KEY `EventId` (`EventId`), KEY `MonitorId` (`MonitorId`), KEY `ZoneId` (`ZoneId`) @@ -745,7 +749,7 @@ insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, N -- -- Create a default admin user. -- -insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','','',0,1); +insert into Users VALUES (NULL,'admin','$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6','',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','','',0,1); -- -- Add a sample filter to purge the oldest 100 events when the disk is 95% full diff --git a/db/zm_update-1.33.12.sql b/db/zm_update-1.33.12.sql new file mode 100644 index 000000000..8188ad841 --- /dev/null +++ b/db/zm_update-1.33.12.sql @@ -0,0 +1,27 @@ +-- +-- Add primary keys for Logs and Stats tables +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Logs' + AND column_name = 'Id' + ) > 0, +"SELECT 'Column Id already exists in Logs'", +"ALTER TABLE `Logs` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Stats' + AND column_name = 'Id' + ) > 0, +"SELECT 'Column Id already exists in Stats'", +"ALTER TABLE `Stats` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index c8413d6a0..d064c3b94 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -23,7 +23,7 @@ %global _hardened_build 1 Name: zoneminder -Version: 1.33.9 +Version: 1.33.12 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -411,6 +411,9 @@ EOF %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog +* Sun Jul 07 2019 Andrew Bauer - 1.33.12-1 +- Bump to 1.33.12 Development + * Sun Jun 23 2019 Andrew Bauer - 1.33.9-1 - Bump to 1.33.9 Development diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index 6527742c8..b572d40fa 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -728,6 +728,7 @@ sub substituteTags { } if ( $text =~ s/%EIMOD%//g ) { + $text =~ s/%EIMOD%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=objdetect/g; my $path = $Event->Path().'/objdetect.jpg'; if ( -e $path ) { push @$attachments_ref, { type=>'image/jpeg', path=>$path }; diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 029492f90..1df9b7718 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -21,6 +21,9 @@ #include "zm_ffmpeg.h" #include "zm_image.h" #include "zm_rgb.h" +extern "C" { +#include "libavutil/pixdesc.h" +} #if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE @@ -70,14 +73,14 @@ static bool bInit = false; void FFMPEGInit() { if ( !bInit ) { - if ( logDebugging() ) + if ( logDebugging() && config.log_ffmpeg ) { av_log_set_level( AV_LOG_DEBUG ); - else + av_log_set_callback(log_libav_callback); + Info("Enabling ffmpeg logs, as LOG_DEBUG+LOG_FFMPEG are enabled in options"); + } else { + Info("Not enabling ffmpeg logs, as LOG_FFMPEG and/or LOG_DEBUG is disabled in options, or this monitor not part of your debug targets"); av_log_set_level( AV_LOG_QUIET ); - if ( config.log_ffmpeg ) - av_log_set_callback(log_libav_callback); - else - Info("Not enabling ffmpeg logs, as LOG_FFMPEG is disabled in options"); + } #if LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0) #else av_register_all(); @@ -295,25 +298,6 @@ void zm_dump_video_frame(const AVFrame *frame, const char *text) { frame->pts ); } -void zm_dump_frame(const AVFrame *frame,const char *text) { - Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d" - " duration %" PRId64 - " layout %d pts %" PRId64, - text, - frame->format, - av_get_sample_fmt_name((AVSampleFormat)frame->format), - frame->sample_rate, - frame->nb_samples, -#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) - frame->channels, - frame->pkt_duration, -#else -0, 0, -#endif - frame->channel_layout, - frame->pts - ); -} #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar ( const AVCodecParameters *par ) { @@ -536,6 +520,45 @@ int zm_receive_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet) return 0; } // end int zm_receive_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet) +int zm_send_frame(AVCodecContext *ctx, AVFrame *frame, AVPacket &packet) { + int ret; + #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + if ( (ret = avcodec_send_frame(ctx, frame)) < 0 ) { + Error("Could not send frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&packet); + return 0; + } + + if ( (ret = avcodec_receive_packet(ctx, &packet)) < 0 ) { + if ( AVERROR(EAGAIN) == ret ) { + // The codec may need more samples than it has, perfectly valid + Debug(2, "Codec not ready to give us a packet"); + } else { + Error("Could not recieve packet (error %d = '%s')", ret, + av_make_error_string(ret).c_str()); + } + zm_av_packet_unref(&packet); + return 0; + } + #else + int data_present; + if ( (ret = avcodec_encode_audio2( + ctx, &packet, frame, &data_present)) < 0 ) { + Error("Could not encode frame (error '%s')", + av_make_error_string(ret).c_str()); + zm_av_packet_unref(&packet); + return 0; + } + if ( !data_present ) { + Debug(2, "Not ready to out a frame yet."); + zm_av_packet_unref(&packet); + return 0; + } + #endif + return 1; +} // wend zm_send_frame + void dumpPacket(AVStream *stream, AVPacket *pkt, const char *text) { char b[10240]; diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index e3b8314f8..7618a461b 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -299,7 +299,36 @@ void zm_dump_codec(const AVCodecContext *codec); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) void zm_dump_codecpar(const AVCodecParameters *par); #endif -void zm_dump_frame(const AVFrame *frame, const char *text="Frame"); +#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) +#define zm_dump_frame(frame, text) Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d" \ + " duration %" PRId64 \ + " layout %d pts %" PRId64,\ + text, \ + frame->format, \ + av_get_sample_fmt_name((AVSampleFormat)frame->format), \ + frame->sample_rate, \ + frame->nb_samples, \ + frame->channels, \ + frame->pkt_duration, \ + frame->channel_layout, \ + frame->pts \ + ); +#else +#define zm_dump_frame(frame, text) Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d" \ + " duration %" PRId64 \ + " layout %d pts %" PRId64, \ + text, \ + frame->format, \ + av_get_sample_fmt_name((AVSampleFormat)frame->format), \ + frame->sample_rate, \ + frame->nb_samples, \ + 0, 0, \ + frame->channel_layout, \ + frame->pts \ + ); + +#endif + void zm_dump_video_frame(const AVFrame *frame, const char *text="Frame"); #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) @@ -332,7 +361,9 @@ bool is_audio_stream(AVStream *); bool is_video_context(AVCodec *); bool is_audio_context(AVCodec *); -int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); +int zm_receive_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet); +int zm_send_frame(AVCodecContext *context, AVFrame *frame, AVPacket &packet); + void dumpPacket(AVStream *, AVPacket *,const char *text=""); void dumpPacket(AVPacket *,const char *text=""); #endif // ZM_FFMPEG_H diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 9cf2d93ef..8c61dfcff 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -29,6 +29,7 @@ extern "C" { #if HAVE_LIBAVUTIL_HWCONTEXT_H #include "libavutil/hwcontext.h" #endif +#include "libavutil/pixdesc.h" } #ifndef AV_ERROR_MAX_STRING_SIZE #define AV_ERROR_MAX_STRING_SIZE 64 @@ -135,7 +136,6 @@ FfmpegCamera::FfmpegCamera( } hwaccel = false; - hwFrame = NULL; mFormatContext = NULL; mVideoStreamId = -1; @@ -153,6 +153,8 @@ FfmpegCamera::FfmpegCamera( packetqueue = NULL; error_count = 0; #if HAVE_LIBAVUTIL_HWCONTEXT_H + hwFrame = NULL; + hw_device_ctx = NULL; hw_pix_fmt = AV_PIX_FMT_NONE; #endif @@ -455,36 +457,46 @@ int FfmpegCamera::OpenFfmpeg() { if ( !config ) { Debug(1, "Decoder %s does not support device type %s.", mVideoCodec->name, av_hwdevice_get_type_name(type)); - return -1; + break; } if ( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) && (config->device_type == type) ) { hw_pix_fmt = config->pix_fmt; break; + } else { + Debug(1, "decoder %s hwConfig doesn't match our type: %s, pix_fmt %s.", + mVideoCodec->name, + av_hwdevice_get_type_name(config->device_type), + av_get_pix_fmt_name(config->pix_fmt) + ); } } // end foreach hwconfig #else hw_pix_fmt = find_fmt_by_hw_type(type); #endif -Debug(1, "Selected gw_pix_fmt %d %s", - hw_pix_fmt, - av_get_pix_fmt_name(hw_pix_fmt)); + if ( hw_pix_fmt != AV_PIX_FMT_NONE ) { + Debug(1, "Selected gw_pix_fmt %d %s", + hw_pix_fmt, + av_get_pix_fmt_name(hw_pix_fmt)); - mVideoCodecContext->get_format = get_hw_format; + mVideoCodecContext->get_format = get_hw_format; - Debug(1, "Creating hwdevice for %s", - (hwaccel_device != "" ? hwaccel_device.c_str() : "")); - ret = av_hwdevice_ctx_create(&hw_device_ctx, type, - (hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0); - if ( ret < 0 ) { - Error("Failed to create specified HW device."); - return -1; + Debug(1, "Creating hwdevice for %s", + (hwaccel_device != "" ? hwaccel_device.c_str() : "")); + ret = av_hwdevice_ctx_create(&hw_device_ctx, type, + (hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0); + if ( ret < 0 ) { + Error("Failed to create specified HW device."); + return -1; + } + Debug(1, "Created hwdevice"); + mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx); + hwaccel = true; + hwFrame = zm_av_frame_alloc(); + } else { + Debug(1, "Failed to setup hwaccel."); } - Debug(1, "Created hwdevice"); - mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx); - hwaccel = true; - hwFrame = zm_av_frame_alloc(); #else Warning("HWAccel support not compiled in."); #endif @@ -637,6 +649,12 @@ int FfmpegCamera::Close() { av_frame_free(&mRawFrame); mRawFrame = NULL; } +#if HAVE_LIBAVUTIL_HWCONTEXT_H + if ( hwFrame ) { + av_frame_free(&hwFrame); + hwFrame = NULL; + } +#endif #if HAVE_LIBSWSCALE if ( mConvertContext ) { @@ -665,6 +683,12 @@ int FfmpegCamera::Close() { mAudioCodecContext = NULL; // Freed by av_close_input_file } +#if HAVE_LIBAVUTIL_HWCONTEXT_H + if ( hw_device_ctx ) { + av_buffer_unref(&hw_device_ctx); + } +#endif + if ( mFormatContext ) { #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) av_close_input_file(mFormatContext); diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index 5cca6b35b..03d89348d 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -7,8 +7,7 @@ FFmpeg_Input::FFmpeg_Input() { input_format_context = NULL; video_stream_id = -1; audio_stream_id = -1; - av_register_all(); - avcodec_register_all(); + FFMPEGInit(); streams = NULL; frame = NULL; } diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 35f25f7c9..b873b8547 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -157,6 +157,7 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { std::pair ans = verifyToken(jwt_token_str, key); std::string username = ans.first; unsigned int iat = ans.second; + Debug (1,"retrieved user '%s' from token", username.c_str()); if (username != "") { char sql[ZM_SQL_MED_BUFSIZ] = ""; @@ -178,7 +179,7 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { if ( n_users != 1 ) { mysql_free_result(result); - Error("Unable to authenticate user %s", username.c_str()); + Error("Unable to authenticate user '%s'", username.c_str()); return NULL; } @@ -188,12 +189,12 @@ User *zmLoadTokenUser (std::string jwt_token_str, bool use_remote_addr ) { if (stored_iat > iat ) { // admin revoked tokens mysql_free_result(result); - Error("Token was revoked for %s", username.c_str()); + Error("Token was revoked for '%s'", username.c_str()); return NULL; } Debug (1,"Got stored expiry time of %u",stored_iat); - Info ("Authenticated user '%s' via token", username.c_str()); + Debug (1,"Authenticated user '%s' via token", username.c_str()); mysql_free_result(result); return user; diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index bd4f47a2f..563c06be8 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -308,7 +308,6 @@ VideoStore::VideoStore( #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) audio_out_stream = avformat_new_stream(oc, NULL); - audio_out_stream->time_base = audio_in_stream->time_base; audio_out_ctx = avcodec_alloc_context3(audio_out_codec); if ( !audio_out_ctx ) { Error("could not allocate codec ctx for AAC"); @@ -476,7 +475,51 @@ VideoStore::~VideoStore() { break; } #endif + + // Need to adjust pts and dts and duration + pkt.stream_index = audio_out_stream->index; + + pkt.duration = av_rescale_q( + pkt.duration, + audio_out_ctx->time_base, + audio_out_stream->time_base); + // Scale the PTS of the outgoing packet to be the correct time base + if ( pkt.pts != AV_NOPTS_VALUE ) { + pkt.pts = av_rescale_q( + pkt.pts, + audio_out_ctx->time_base, + audio_in_stream->time_base); + // audio_first_pts is in audio_in_stream time base + pkt.pts -= audio_first_pts; + pkt.pts = av_rescale_q( + pkt.pts, + audio_in_stream->time_base, + audio_out_stream->time_base); + + Debug(2, "audio pkt.pts = %" PRId64 " from first_pts(%" PRId64 ")", + pkt.pts, audio_first_pts); + } else { + Debug(2, "pkt.pts = undef"); + pkt.pts = AV_NOPTS_VALUE; + } + + if ( pkt.dts != AV_NOPTS_VALUE ) { + pkt.dts = av_rescale_q( + pkt.dts, + audio_out_ctx->time_base, + audio_in_stream->time_base); + pkt.dts -= audio_first_dts; + pkt.dts = av_rescale_q( + pkt.dts, + audio_in_ctx->time_base, + audio_out_stream->time_base); + Debug(2, "pkt.dts = %" PRId64 " - first_dts(%" PRId64 ")", + pkt.dts, audio_first_dts); + } else { + pkt.dts = AV_NOPTS_VALUE; + } + dumpPacket(audio_out_stream, &pkt, "writing flushed packet"); av_interleaved_write_frame(oc, &pkt); zm_av_packet_unref(&pkt); @@ -1017,8 +1060,8 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { dumpPacket(audio_in_stream, ipkt, "input packet"); if ( audio_out_codec ) { - Debug(2, "Have output codec"); if ( ( ret = zm_receive_frame(audio_in_ctx, in_frame, *ipkt) ) < 0 ) { + Debug(3, "Not ready to receive frame"); return 0; } @@ -1030,13 +1073,15 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { } zm_dump_frame(out_frame, "Out frame after resample"); +#if 0 // out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder if ( out_frame->pts != AV_NOPTS_VALUE ) { if ( !audio_first_pts ) { audio_first_pts = out_frame->pts; - Debug(1, "No video_first_pts setting to %" PRId64, audio_first_pts); + Debug(1, "No audio_first_pts setting to %" PRId64, audio_first_pts); out_frame->pts = 0; } else { + // out_frame_pts is in codec->timebase, audio_first_pts is in packet timebase. out_frame->pts = out_frame->pts - audio_first_pts; zm_dump_frame(out_frame, "Out frame after pts adjustment"); } @@ -1046,56 +1091,57 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { out_frame->pts = audio_next_pts; } audio_next_pts = out_frame->pts + out_frame->nb_samples; +#endif av_init_packet(&opkt); - #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( (ret = avcodec_send_frame(audio_out_ctx, out_frame)) < 0 ) { - Error("Could not send frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); + if ( !zm_send_frame(audio_out_ctx, out_frame, opkt) ) { return 0; } - if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { - if ( AVERROR(EAGAIN) == ret ) { - // The codec may need more samples than it has, perfectly valid - Debug(2, "Codec not ready to give us a packet"); - } else { - Error("Could not recieve packet (error %d = '%s')", ret, - av_make_error_string(ret).c_str()); - } - zm_av_packet_unref(&opkt); - return 0; - } - #else - int data_present; - if ( (ret = avcodec_encode_audio2( - audio_out_ctx, &opkt, out_frame, &data_present)) < 0 ) { - Error("Could not encode frame (error '%s')", - av_make_error_string(ret).c_str()); - zm_av_packet_unref(&opkt); - return 0; - } - if ( !data_present ) { - Debug(2, "Not ready to out a frame yet."); - zm_av_packet_unref(&opkt); - return 0; - } - #endif -#if 0 - // These should be set by encoder. They may not directly relate to ipkt due to buffering in codec. - opkt.duration = av_rescale_q(opkt.duration, - audio_in_stream->time_base, - audio_out_stream->time_base); - opkt.pts = av_rescale_q(opkt.pts, - audio_in_stream->time_base, - audio_out_stream->time_base); - opkt.dts = av_rescale_q(opkt.dts, - audio_in_stream->time_base, - audio_out_stream->time_base); -#endif dumpPacket(audio_out_stream, &opkt, "raw opkt"); + opkt.duration = av_rescale_q( + opkt.duration, + audio_out_ctx->time_base, + audio_out_stream->time_base); + // Scale the PTS of the outgoing packet to be the correct time base + if ( ipkt->pts != AV_NOPTS_VALUE ) { + if ( !audio_first_pts ) { + opkt.pts = 0; + audio_first_pts = ipkt->pts; + Debug(1, "No audio_first_pts"); + } else { + opkt.pts = av_rescale_q( + opkt.pts, + audio_out_ctx->time_base, + audio_out_stream->time_base); + opkt.pts -= audio_first_pts; + Debug(2, "audio opkt.pts = %" PRId64 " from first_pts %" PRId64, + opkt.pts, audio_first_pts); + } + } else { + Debug(2, "opkt.pts = undef"); + opkt.pts = AV_NOPTS_VALUE; + } + + if ( opkt.dts != AV_NOPTS_VALUE ) { + if ( !audio_first_dts ) { + opkt.dts = 0; + audio_first_dts = opkt.dts; + } else { + opkt.dts = av_rescale_q( + opkt.dts, + audio_out_ctx->time_base, + audio_out_stream->time_base); + opkt.dts -= audio_first_dts; + Debug(2, "opkt.dts = %" PRId64 " from first_dts %" PRId64, + opkt.dts, audio_first_dts); + } + audio_last_dts = opkt.dts; + } else { + opkt.dts = AV_NOPTS_VALUE; + } + } else { Debug(2,"copying"); av_init_packet(&opkt); @@ -1128,16 +1174,6 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { } if ( ipkt->dts != AV_NOPTS_VALUE ) { - // So if the in has no dts assigned... still need an out dts... so we use cur_dts? - -#if 0 - if ( audio_last_dts >= audio_in_stream->cur_dts ) { - Debug(1, "Resetting audio_last_dts from (%d) to cur_dts (%d)", audio_last_dts, audio_in_stream->cur_dts); - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); - } else { - opkt.dts = audio_next_dts + av_rescale_q( audio_in_stream->cur_dts - audio_last_dts, AV_TIME_BASE_Q, audio_out_stream->time_base); - } -#endif if ( !audio_first_dts ) { opkt.dts = 0; audio_first_dts = ipkt->dts; @@ -1204,28 +1240,9 @@ int VideoStore::resample_audio() { av_make_error_string(ret).c_str()); return 0; } - zm_dump_frame(out_frame, "Out frame after convert"); - -#if 0 - // out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder - if ( out_frame->pts != AV_NOPTS_VALUE ) { - if ( !video_first_pts ) { - video_first_pts = out_frame->pts; - Debug(1, "No video_first_pts setting to %" PRId64, video_first_pts); - out_frame->pts = 0; - } else { - out_frame->pts = out_frame->pts - video_first_pts; - } - // - } else { - // sending AV_NOPTS_VALUE doesn't really work but we seem to get it in ffmpeg 2.8 - out_frame->pts = audio_next_pts; - } - audio_next_pts = out_frame->pts + out_frame->nb_samples; -#endif - - if ((ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples)) < 0) { + ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples); + if ( ret < 0 ) { Error("Could not reallocate FIFO"); return 0; } @@ -1240,7 +1257,7 @@ int VideoStore::resample_audio() { // Reset frame_size to output_frame_size int frame_size = audio_out_ctx->frame_size; - // AAC requires 1024 samples per encode. Our input tends to be 160, so need to buffer them. + // AAC requires 1024 samples per encode. Our input tends to be something else, so need to buffer them. if ( frame_size > av_audio_fifo_size(fifo) ) { return 0; } @@ -1251,16 +1268,14 @@ int VideoStore::resample_audio() { } out_frame->nb_samples = frame_size; // resampling changes the duration because the timebase is 1/samples + // I think we should be dealing in codec timebases not stream if ( in_frame->pts != AV_NOPTS_VALUE ) { - out_frame->pkt_duration = av_rescale_q( - in_frame->pkt_duration, - audio_in_stream->time_base, - audio_out_stream->time_base); out_frame->pts = av_rescale_q( in_frame->pts, - audio_in_stream->time_base, - audio_out_stream->time_base); + audio_in_ctx->time_base, + audio_out_ctx->time_base); } + zm_dump_frame(out_frame, "Out frame after timestamp conversion"); #else #if defined(HAVE_LIBAVRESAMPLE) ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, diff --git a/src/zms.cpp b/src/zms.cpp index 5e6e4c2d6..c78dba1d2 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -43,9 +43,8 @@ bool ValidateAccess( User *user, int mon_id ) { allowed = false; } if ( !allowed ) { - Error( "Error, insufficient privileges for requested action user %d %s for monitor %d", - user->Id(), user->getUsername(), mon_id ); - exit( -1 ); + Error("Error, insufficient privileges for requested action user %d %s for monitor %d", + user->Id(), user->getUsername(), mon_id); } return allowed; } @@ -164,8 +163,7 @@ int main( int argc, const char *argv[] ) { strncpy( auth, value, sizeof(auth)-1 ); } else if ( !strcmp( name, "token" ) ) { jwt_token_str = value; - Debug(1,"ZMS: JWT token found: %s", jwt_token_str.c_str()); - + Debug(1, "ZMS: JWT token found: %s", jwt_token_str.c_str()); } else if ( !strcmp( name, "user" ) ) { username = UriDecode( value ); } else if ( !strcmp( name, "pass" ) ) { @@ -184,17 +182,15 @@ int main( int argc, const char *argv[] ) { } else { snprintf(log_id_string, sizeof(log_id_string), "zms_e%" PRIu64, event_id); } - logInit( log_id_string ); + logInit(log_id_string); if ( config.opt_use_auth ) { User *user = 0; - if (jwt_token_str != "") { + if ( jwt_token_str != "" ) { //user = zmLoadTokenUser(jwt_token_str, config.auth_hash_ips); user = zmLoadTokenUser(jwt_token_str, false); - - } - else if ( strcmp(config.auth_relay, "none") == 0 ) { + } else if ( strcmp(config.auth_relay, "none") == 0 ) { if ( checkUser(username.c_str()) ) { user = zmLoadUser(username.c_str()); } else { @@ -216,21 +212,27 @@ int main( int argc, const char *argv[] ) { } } if ( !user ) { + fprintf(stdout, "HTTP/1.0 401 Unauthorized\r\n"); Error("Unable to authenticate user"); logTerm(); zmDbClose(); return -1; } - ValidateAccess(user, monitor_id); + if ( !ValidateAccess(user, monitor_id) ) { + fprintf(stdout, "HTTP/1.0 403 Forbidden\r\n"); + logTerm(); + zmDbClose(); + return -1; + } } // end if config.opt_use_auth hwcaps_detect(); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); - setbuf( stdout, 0 ); + setbuf(stdout, 0); if ( nph ) { - fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); + fprintf(stdout, "HTTP/1.0 200 OK\r\n"); } fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index fc1eaaeff..7addf8362 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -333,7 +333,7 @@ EOF fi; else echo "dputting to $PPA"; - dput $PPA $SC + dput $PPA $SC fi; fi; done; # foreach distro @@ -345,5 +345,3 @@ if [ "$INTERACTIVE" != "no" ]; then else rm -fr "$DIRECTORY.orig"; echo "The checked out copy has been deleted"; fi - - diff --git a/version b/version index ca7a1ec9c..325da8275 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.33.11 +1.33.12 diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index f83aecf97..0686232df 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -254,6 +254,7 @@ class MonitorsController extends AppController { throw new BadRequestException(__('Invalid command')); } $zm_path_bin = Configure::read('ZM_PATH_BIN'); + $mToken = $this->request->query('token') ? $this->request->query('token') : null; switch ($cmd) { case 'on': @@ -281,8 +282,12 @@ class MonitorsController extends AppController { $zmAuthRelay = $config['Config']['Value']; $auth = ''; + if ( $zmOptAuth ) { - if ( $zmAuthRelay == 'hashed' ) { + if ($mToken) { + $auth = ' -T '.$mToken; + } + elseif ( $zmAuthRelay == 'hashed' ) { $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET')); $config = $this->Config->find('first', $options); $zmAuthHashSecret = $config['Config']['Value']; diff --git a/web/includes/Event.php b/web/includes/Event.php index 07460a7ec..1b996a839 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -580,6 +580,9 @@ class Event { if ( file_exists( $this->Path().'/'.$this->DefaultVideo() ) ) { return true; } + if ( !defined('ZM_SERVER_ID') ) { + return false; + } $Storage= $this->Storage(); $Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server(); if ( $Server->Id() != ZM_SERVER_ID ) { @@ -624,6 +627,9 @@ class Event { if ( file_exists($this->Path().'/'.$this->DefaultVideo()) ) { return filesize($this->Path().'/'.$this->DefaultVideo()); } + if ( !defined('ZM_SERVER_ID') ) { + return false; + } $Storage= $this->Storage(); $Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server(); if ( $Server->Id() != ZM_SERVER_ID ) { diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 75c432c09..c46048716 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -105,6 +105,7 @@ private $defaults = array( 'DefaultCodec' => 'auto', ); private $status_fields = array( + 'Status' => null, 'AnalysisFPS' => null, 'CaptureFPS' => null, 'CaptureBandwidth' => null, @@ -271,17 +272,27 @@ private $control_fields = array( return $this->{$fn}; #array_unshift($args, $this); #call_user_func_array( $this->{$fn}, $args); - } else { - if ( array_key_exists($fn, $this->control_fields) ) { + } else if ( array_key_exists($fn, $this->control_fields) ) { return $this->control_fields{$fn}; - } else if ( array_key_exists( $fn, $this->defaults ) ) { + } else if ( array_key_exists( $fn, $this->defaults ) ) { return $this->defaults{$fn}; + } else if ( array_key_exists( $fn, $this->status_fields) ) { + $sql = 'SELECT Status,CaptureFPS,AnalysisFPS,CaptureBandwidth + FROM Monitor_Status WHERE MonitorId=?'; + $row = dbFetchOne($sql, NULL, array($this->{'Id'})); + if ( !$row ) { + Error("Unable to load Monitor record for Id=" . $this->{'Id'}); } else { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Warning( "Unknown function call Monitor->$fn from $file:$line" ); + foreach ($row as $k => $v) { + $this->{$k} = $v; + } } + return $this->{$fn}; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Warning( "Unknown function call Monitor->$fn from $file:$line" ); } } diff --git a/web/includes/functions.php b/web/includes/functions.php index e1016bb82..867861ffe 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -2459,11 +2459,13 @@ function do_post_request($url, $data, $optional_headers = null) { $ctx = stream_context_create($params); $fp = @fopen($url, 'rb', false, $ctx); if ( !$fp ) { - throw new Exception("Problem with $url, $php_errormsg"); + throw new Exception("Problem with $url, " + .print_r(error_get_last(),true)); } $response = @stream_get_contents($fp); if ( $response === false ) { - throw new Exception("Problem reading data from $url, $php_errormsg"); + throw new Exception("Problem reading data from $url, data: ".print_r($params,true) + .print_r(error_get_last(),true)); } return $response; } diff --git a/web/skins/classic/js/skin.js b/web/skins/classic/js/skin.js index a14c63bfc..c29ef4a84 100644 --- a/web/skins/classic/js/skin.js +++ b/web/skins/classic/js/skin.js @@ -51,51 +51,51 @@ function newWindow( url, name, width, height ) { function getPopupSize( tag, width, height ) { if ( typeof popupSizes == 'undefined' ) { - Error( "Can't find any window sizes" ); - return ( {'width': 0, 'height': 0} ); + Error("Can't find any window sizes"); + return {'width': 0, 'height': 0}; } - var popupSize = Object.clone( popupSizes[tag] ); + var popupSize = Object.clone(popupSizes[tag]); if ( !popupSize ) { - Error( "Can't find window size for tag '"+tag+"'" ); - return ( {'width': 0, 'height': 0} ); + Error("Can't find window size for tag '"+tag+"'"); + return {'width': 0, 'height': 0}; } if ( popupSize.width && popupSize.height ) { if ( width || height ) { - Warning( "Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'" ); + Warning("Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'"); } - return ( popupSize ); + return popupSize; } if ( popupSize.addWidth ) { popupSize.width = popupSize.addWidth; if ( !width ) { - Error( "Got addWidth but no passed width when getting popup size for tag '"+tag+"'" ); + Error("Got addWidth but no passed width when getting popup size for tag '"+tag+"'"); } else { popupSize.width += parseInt(width); } } else if ( width ) { popupSize.width = width; - Error( "Got passed width but no addWidth when getting popup size for tag '"+tag+"'" ); + Error("Got passed width but no addWidth when getting popup size for tag '"+tag+"'"); } if ( popupSize.minWidth && popupSize.width < popupSize.minWidth ) { - Warning( "Adjusting to minimum width when getting popup size for tag '"+tag+"'" ); + Warning("Adjusting to minimum width when getting popup size for tag '"+tag+"'"); popupSize.width = popupSize.minWidth; } if ( popupSize.addHeight ) { popupSize.height = popupSize.addHeight; if ( !height ) { - Error( "Got addHeight but no passed height when getting popup size for tag '"+tag+"'" ); + Error("Got addHeight but no passed height when getting popup size for tag '"+tag+"'"); } else { popupSize.height += parseInt(height); } } else if ( height ) { popupSize.height = height; - Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" ); + Error("Got passed height but no addHeight when getting popup size for tag '"+tag+"'"); } if ( popupSize.minHeight && ( popupSize.height < popupSize.minHeight ) ) { - Warning( "Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height ); + Warning("Adjusting to minimum height ("+popupSize.minHeight+") when getting popup size for tag '"+tag+"' because calculated height is " + popupSize.height); popupSize.height = popupSize.minHeight; } - return ( popupSize ); + return popupSize; } function zmWindow() { @@ -144,7 +144,7 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { el.addEventListener("click", function onClick(evt) { var el = this; var url; - if (el.hasAttribute("href")) { + if ( el.hasAttribute("href") ) { // url = el.getAttribute("href"); } else { @@ -167,12 +167,20 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { // 'data-on-click-this' calls the global function in the attribute value with the element when a click happens. document.querySelectorAll("a[data-on-click-this], button[data-on-click-this], input[data-on-click-this]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click-this"); + if ( !window[fnName] ) { + console.error("Nothing found to bind to " + fnName); + return; + } el.onclick = window[fnName].bind(el, el); }); // 'data-on-click' calls the global function in the attribute value with no arguments when a click happens. document.querySelectorAll("a[data-on-click], button[data-on-click], input[data-on-click]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click"); + if ( !window[fnName] ) { + console.error("Nothing found to bind to " + fnName); + return; + } el.onclick = function() { window[fnName](); }; @@ -181,6 +189,10 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { // 'data-on-click-true' calls the global function in the attribute value with no arguments when a click happens. document.querySelectorAll("a[data-on-click-true], button[data-on-click-true], input[data-on-click-true]").forEach(function attachOnClick(el) { var fnName = el.getAttribute("data-on-click-true"); + if ( !window[fnName] ) { + console.error("Nothing found to bind to " + fnName); + return; + } el.onclick = function() { window[fnName](true); }; @@ -189,12 +201,20 @@ window.addEventListener("DOMContentLoaded", function onSkinDCL() { // 'data-on-change-this' calls the global function in the attribute value with the element when a change happens. document.querySelectorAll("select[data-on-change-this], input[data-on-change-this]").forEach(function attachOnChangeThis(el) { var fnName = el.getAttribute("data-on-change-this"); + if ( !window[fnName] ) { + console.error("Nothing found to bind to " + fnName); + return; + } el.onchange = window[fnName].bind(el, el); }); // 'data-on-change' adds an event listener for the global function in the attribute value when a change happens. document.querySelectorAll("select[data-on-change], input[data-on-change]").forEach(function attachOnChange(el) { var fnName = el.getAttribute("data-on-change"); + if ( !window[fnName] ) { + console.error("Nothing found to bind to " + fnName); + return; + } el.onchange = window[fnName]; }); }); diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index 703e67153..fc692169b 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -215,8 +215,12 @@ while ( $event_row = dbFetchNext($results) ) { ( $event->EndTime() ? ' until ' . strftime(STRF_FMT_DATETIME_SHORTER, strtotime($event->EndTime()) ) : '' ) ?> Length() ) ?> - Id(), 'zmFrames', 'frames', $event->Frames() ) ?> - Id(), 'zmFrames', 'frames', $event->AlarmFrames() ) ?> + Id(), 'zmFrames', + ( ZM_WEB_LIST_THUMBS ? array('frames', ZM_WEB_LIST_THUMB_WIDTH, ZM_WEB_LIST_THUMB_HEIGHT) : 'frames'), + $event->Frames() ) ?> + Id(), 'zmFrames', + ( ZM_WEB_LIST_THUMBS ? array('frames', ZM_WEB_LIST_THUMB_WIDTH, ZM_WEB_LIST_THUMB_HEIGHT) : 'frames'), + $event->AlarmFrames() ) ?> TotScore() ?> AvgScore() ?> Monitor(); $countSql = 'SELECT COUNT(*) AS FrameCount FROM Frames AS F WHERE 1 '; $frameSql = 'SELECT *, unix_timestamp( TimeStamp ) AS UnixTimeStamp FROM Frames AS F WHERE 1 '; -$eid = $_REQUEST['eid']; // override the sort_field handling in parseSort for frames if ( empty($_REQUEST['sort_field']) ) @@ -59,8 +61,6 @@ if ( $_REQUEST['filter']['sql'] ) { $frameSql .= " ORDER BY $sortColumn $sortOrder,Id $sortOrder"; -$Event = new ZM\Event($eid); -$Monitor = $Event->Monitor(); if ( isset( $_REQUEST['scale'] ) ) { $scale = validNum($_REQUEST['scale']); @@ -75,22 +75,22 @@ if ( isset( $_REQUEST['scale'] ) ) { $page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 1; $limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0; -$nEvents = dbFetchOne($countSql, 'FrameCount' ); +$nFrames = dbFetchOne($countSql, 'FrameCount' ); -if ( !empty($limit) && $nEvents > $limit ) { - $nEvents = $limit; +if ( !empty($limit) && ($nFrames > $limit) ) { + $nFrames = $limit; } -$pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE); +$pages = (int)ceil($nFrames/ZM_WEB_EVENTS_PER_PAGE); if ( !empty($page) ) { - if ( $page < 0 ) + if ( $page <= 0 ) $page = 1; else if ( $pages and ( $page > $pages ) ) $page = $pages; $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); - if ( empty( $limit ) ) { + if ( empty($limit) ) { $limitAmount = ZM_WEB_EVENTS_PER_PAGE; } else { $limitLeft = $limit - $limitStart; @@ -104,7 +104,7 @@ if ( !empty($page) ) { $maxShortcuts = 5; $pagination = getPagination($pages, $page, $maxShortcuts, $sortQuery.'&eid='.$eid.$limitQuery.$filterQuery); -$frames = dbFetchAll( $frameSql ); +$frames = dbFetchAll($frameSql); $focusWindow = true; diff --git a/web/skins/classic/views/report_event_audit.php b/web/skins/classic/views/report_event_audit.php index c7517728e..f3f508623 100644 --- a/web/skins/classic/views/report_event_audit.php +++ b/web/skins/classic/views/report_event_audit.php @@ -192,9 +192,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ) ) ); + parseFilter($ZeroSize_filter); } - - ?> @@ -219,7 +218,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { '.count($FileMissing).'' : '0' ?> - '.count($FileMissing).'' : '0' ?> + '.count($ZeroSize).'' : '0' ?> Type() == 'Local' ) {
Status() != 'Capturing' ) { +if ( $monitor->Status() != 'Connected' ) { echo '
Monitor is not capturing. We will be unable to provide an image
'; } ?>