Merge branch 'storageareas' of github.com:ConnorTechnology/ZoneMinder into storageareas

This commit is contained in:
Isaac Connor 2019-07-02 12:40:29 -04:00
commit ef0f5212f2
14 changed files with 650 additions and 511 deletions

View File

@ -82,8 +82,7 @@ a read.
:: ::
gunzip /usr/share/doc/zoneminder/README.Debian.gz zcat /usr/share/doc/zoneminder/README.Debian.gz
cat /usr/share/doc/zoneminder/README.Debian
**Step 7:** Enable ZoneMinder service **Step 7:** Enable ZoneMinder service
@ -209,8 +208,7 @@ a read.
:: ::
gunzip /usr/share/doc/zoneminder/README.Debian.gz zcat /usr/share/doc/zoneminder/README.Debian.gz
cat /usr/share/doc/zoneminder/README.Debian
**Step 7:** Setup Database **Step 7:** Setup Database

View File

@ -20,10 +20,10 @@ add_executable(zms zms.cpp)
include_directories(libbcrypt/include/bcrypt) include_directories(libbcrypt/include/bcrypt)
include_directories(jwt-cpp/include/jwt-cpp) include_directories(jwt-cpp/include/jwt-cpp)
target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} ${CMAKE_DL_LIBS})
target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} ${CMAKE_DL_LIBS})
target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} bcrypt) target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} ${CMAKE_DL_LIBS} bcrypt)
target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} bcrypt) target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} ${CMAKE_DL_LIBS} bcrypt)
# Generate man files for the binaries destined for the bin folder # Generate man files for the binaries destined for the bin folder
FOREACH(CBINARY zma zmc zmu) FOREACH(CBINARY zma zmc zmu)

View File

@ -1,5 +1,5 @@
// //
// ZoneMinder Ffmpeg Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $ // ZoneMinder Ffmpeg Camera Class Implementation
// Copyright (C) 2001-2008 Philip Coombes // Copyright (C) 2001-2008 Philip Coombes
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
@ -39,6 +39,7 @@ extern "C" {
#include <signal.h> #include <signal.h>
#include <pthread.h> #include <pthread.h>
#endif #endif
#include <string>
#if HAVE_LIBAVUTIL_HWCONTEXT_H #if HAVE_LIBAVUTIL_HWCONTEXT_H
@ -54,9 +55,11 @@ static enum AVPixelFormat get_hw_format(
return *p; return *p;
} }
Error("Failed to get HW surface format for %s.", av_get_pix_fmt_name(hw_pix_fmt)); Error("Failed to get HW surface format for %s.",
av_get_pix_fmt_name(hw_pix_fmt));
for ( p = pix_fmts; *p != -1; p++ ) for ( p = pix_fmts; *p != -1; p++ )
Error("Available HW surface format was %s.", av_get_pix_fmt_name(*p)); Error("Available HW surface format was %s.",
av_get_pix_fmt_name(*p));
return AV_PIX_FMT_NONE; return AV_PIX_FMT_NONE;
} }
@ -106,8 +109,7 @@ FfmpegCamera::FfmpegCamera(
bool p_capture, bool p_capture,
bool p_record_audio, bool p_record_audio,
const std::string &p_hwaccel_name, const std::string &p_hwaccel_name,
const std::string &p_hwaccel_device const std::string &p_hwaccel_device) :
) :
Camera( Camera(
p_id, p_id,
FFMPEG_SRC, FFMPEG_SRC,
@ -145,7 +147,6 @@ FfmpegCamera::FfmpegCamera(
mRawFrame = NULL; mRawFrame = NULL;
mFrame = NULL; mFrame = NULL;
frameCount = 0; frameCount = 0;
startTime = 0;
mCanCapture = false; mCanCapture = false;
videoStore = NULL; videoStore = NULL;
have_video_keyframe = false; have_video_keyframe = false;
@ -158,7 +159,8 @@ FfmpegCamera::FfmpegCamera(
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
mConvertContext = NULL; mConvertContext = NULL;
#endif #endif
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ /* Has to be located inside the constructor so other components such as zma
* will receive correct colours and subpixel order */
if ( colours == ZM_COLOUR_RGB32 ) { if ( colours == ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA; subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA; imagePixFormat = AV_PIX_FMT_RGBA;
@ -174,7 +176,6 @@ FfmpegCamera::FfmpegCamera(
} // FfmpegCamera::FfmpegCamera } // FfmpegCamera::FfmpegCamera
FfmpegCamera::~FfmpegCamera() { FfmpegCamera::~FfmpegCamera() {
Close(); Close();
if ( capture ) { if ( capture ) {
@ -204,7 +205,7 @@ int FfmpegCamera::PrimeCapture() {
int FfmpegCamera::PreCapture() { int FfmpegCamera::PreCapture() {
// If Reopen was called, then ffmpeg is closed and we need to reopen it. // If Reopen was called, then ffmpeg is closed and we need to reopen it.
if ( ! mCanCapture ) if ( !mCanCapture )
return OpenFfmpeg(); return OpenFfmpeg();
// Nothing to do here // Nothing to do here
return 0; return 0;
@ -216,15 +217,20 @@ int FfmpegCamera::Capture(Image &image) {
} }
int ret; int ret;
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. // If the reopen thread has a value, but mCanCapture != 0, then we have just
// reopened the connection to the device, and we can clean up the thread.
int frameComplete = false; int frameComplete = false;
while ( !frameComplete && !zm_terminate) { while ( !frameComplete && !zm_terminate ) {
ret = av_read_frame(mFormatContext, &packet); ret = av_read_frame(mFormatContext, &packet);
if ( ret < 0 ) { if ( ret < 0 ) {
if ( if (
// Check if EOF. // Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || (
ret == AVERROR_EOF
||
(mFormatContext->pb && mFormatContext->pb->eof_reached)
) ||
// Check for Connection failure. // Check for Connection failure.
(ret == -110) (ret == -110)
) { ) {
@ -245,7 +251,11 @@ int FfmpegCamera::Capture(Image &image) {
Debug(5, "Got packet from stream %d dts (%d) pts(%d)", Debug(5, "Got packet from stream %d dts (%d) pts(%d)",
packet.stream_index, packet.pts, packet.dts); packet.stream_index, packet.pts, packet.dts);
// What about audio stream? Maybe someday we could do sound detection... // What about audio stream? Maybe someday we could do sound detection...
if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) { if (
(packet.stream_index == mVideoStreamId)
&&
(keyframe || have_video_keyframe)
) {
ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet); ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet);
if ( ret < 0 ) { if ( ret < 0 ) {
Error("Unable to get frame at frame %d: %s, continuing", Error("Unable to get frame at frame %d: %s, continuing",
@ -277,7 +287,6 @@ int FfmpegCamera::PostCapture() {
} }
int FfmpegCamera::OpenFfmpeg() { int FfmpegCamera::OpenFfmpeg() {
int ret; int ret;
have_video_keyframe = false; have_video_keyframe = false;
@ -291,7 +300,7 @@ int FfmpegCamera::OpenFfmpeg() {
AVDictionary *opts = 0; AVDictionary *opts = 0;
ret = av_dict_parse_string(&opts, Options().c_str(), "=", ",", 0); ret = av_dict_parse_string(&opts, Options().c_str(), "=", ",", 0);
if ( ret < 0 ) { if ( ret < 0 ) {
Warning("Could not parse ffmpeg input options list '%s'\n", Options().c_str()); Warning("Could not parse ffmpeg input options '%s'", Options().c_str());
} }
// Set transport method as specified by method field, rtpUni is default // Set transport method as specified by method field, rtpUni is default
@ -307,7 +316,7 @@ int FfmpegCamera::OpenFfmpeg() {
} else { } else {
Warning("Unknown method (%s)", method.c_str()); Warning("Unknown method (%s)", method.c_str());
} }
//#av_dict_set(&opts, "timeout", "10000000", 0); // in microseconds. // #av_dict_set(&opts, "timeout", "10000000", 0); // in microseconds.
if ( ret < 0 ) { if ( ret < 0 ) {
Warning("Could not set rtsp_transport method '%s'", method.c_str()); Warning("Could not set rtsp_transport method '%s'", method.c_str());
@ -315,11 +324,11 @@ int FfmpegCamera::OpenFfmpeg() {
Debug(1, "Calling avformat_open_input for %s", mPath.c_str()); Debug(1, "Calling avformat_open_input for %s", mPath.c_str());
mFormatContext = avformat_alloc_context( ); mFormatContext = avformat_alloc_context();
// Speed up find_stream_info // Speed up find_stream_info
//FIXME can speed up initial analysis but need sensible parameters... // FIXME can speed up initial analysis but need sensible parameters...
//mFormatContext->probesize = 32; // mFormatContext->probesize = 32;
//mFormatContext->max_analyze_duration = 32; // mFormatContext->max_analyze_duration = 32;
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
mFormatContext->interrupt_callback.opaque = this; mFormatContext->interrupt_callback.opaque = this;
@ -327,7 +336,8 @@ int FfmpegCamera::OpenFfmpeg() {
if ( ret != 0 ) if ( ret != 0 )
#endif #endif
{ {
Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(ret)); Error("Unable to open input %s due to: %s", mPath.c_str(),
av_make_error_string(ret).c_str());
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file(mFormatContext); av_close_input_file(mFormatContext);
#else #else
@ -340,7 +350,7 @@ int FfmpegCamera::OpenFfmpeg() {
return -1; return -1;
} }
AVDictionaryEntry *e=NULL; AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning("Option %s not recognized by ffmpeg", e->key); Warning("Option %s not recognized by ffmpeg", e->key);
} }
@ -349,95 +359,76 @@ int FfmpegCamera::OpenFfmpeg() {
Info("Stream open %s, parsing streams...", mPath.c_str()); Info("Stream open %s, parsing streams...", mPath.c_str());
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
Debug(4, "Calling av_find_stream_info"); ret = av_find_stream_info(mFormatContext);
if ( av_find_stream_info(mFormatContext) < 0 )
#else #else
Debug(4, "Calling avformat_find_stream_info"); ret = avformat_find_stream_info(mFormatContext, 0);
if ( avformat_find_stream_info(mFormatContext, 0) < 0 )
#endif #endif
{ if ( ret < 0 ) {
Error("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno)); Error("Unable to find stream info from %s due to: %s", mPath.c_str(),
av_make_error_string(ret).c_str());
return -1; return -1;
} }
startTime = av_gettime();//FIXME here or after find_Stream_info
Debug(4, "Got stream info");
// Find first video stream present // Find first video stream present
// The one we want Might not be the first // The one we want Might not be the first
mVideoStreamId = -1; mVideoStreamId = -1;
mAudioStreamId = -1; mAudioStreamId = -1;
for ( unsigned int i=0; i < mFormatContext->nb_streams; i++ ) { for ( unsigned int i=0; i < mFormatContext->nb_streams; i++ ) {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) AVStream *stream = mFormatContext->streams[i];
if ( mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) { if ( is_video_stream(stream) ) {
#else
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) {
#endif
#endif
if ( mVideoStreamId == -1 ) { if ( mVideoStreamId == -1 ) {
mVideoStreamId = i; mVideoStreamId = i;
// if we break, then we won't find the audio stream // if we break, then we won't find the audio stream
continue; continue;
} else { } else {
Debug(2, "Have another video stream." ); Debug(2, "Have another video stream.");
} }
} } else if ( is_audio_stream(stream) ) {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
if ( mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ) {
#else
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) {
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO ) {
#endif
#endif
if ( mAudioStreamId == -1 ) { if ( mAudioStreamId == -1 ) {
mAudioStreamId = i; mAudioStreamId = i;
} else { } else {
Debug(2, "Have another audio stream." ); Debug(2, "Have another audio stream.");
} }
} }
} // end foreach stream } // end foreach stream
if ( mVideoStreamId == -1 ) if ( mVideoStreamId == -1 )
Fatal("Unable to locate video stream in %s", mPath.c_str()); Fatal("Unable to locate video stream in %s", mPath.c_str());
if ( mAudioStreamId == -1 )
Debug(3, "Unable to locate audio stream in %s", mPath.c_str());
Debug(3, "Found video stream at index %d", mVideoStreamId); Debug(3, "Found video stream at index %d, audio stream at index %d",
Debug(3, "Found audio stream at index %d", mAudioStreamId); mVideoStreamId, mAudioStreamId);
packetqueue = new zm_packetqueue( mVideoStreamId > mAudioStreamId ? mVideoStreamId : mAudioStreamId ); packetqueue = new zm_packetqueue(
(mVideoStreamId > mAudioStreamId) ? mVideoStreamId : mAudioStreamId);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
//mVideoCodecContext = avcodec_alloc_context3(NULL); // mVideoCodecContext = avcodec_alloc_context3(NULL);
//avcodec_parameters_to_context( mVideoCodecContext, mFormatContext->streams[mVideoStreamId]->codecpar ); // avcodec_parameters_to_context(mVideoCodecContext,
// mFormatContext->streams[mVideoStreamId]->codecpar);
// this isn't copied. // this isn't copied.
//mVideoCodecContext->time_base = mFormatContext->streams[mVideoStreamId]->codec->time_base; // mVideoCodecContext->time_base =
// mFormatContext->streams[mVideoStreamId]->codec->time_base;
#else #else
#endif #endif
mVideoCodecContext = mFormatContext->streams[mVideoStreamId]->codec; mVideoCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
// STolen from ispy
//this fixes issues with rtsp streams!! woot.
//mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG2_CHUNKS | CODEC_FLAG_LOW_DELAY; // Enable faster H264 decode.
#ifdef CODEC_FLAG2_FAST #ifdef CODEC_FLAG2_FAST
mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY; mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY;
#endif #endif
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) { if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) {
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) { if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) {
Debug(1, "Failed to find decoder (h264_mmal)" ); Debug(1, "Failed to find decoder (h264_mmal)");
} else { } else {
Debug(1, "Success finding decoder (h264_mmal)" ); Debug(1, "Success finding decoder (h264_mmal)");
} }
} }
if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) { if ( !mVideoCodec ) {
mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id);
if ( !mVideoCodec ) {
// Try and get the codec from the codec context // Try and get the codec from the codec context
Error("Can't find codec for video stream from %s", mPath.c_str()); Error("Can't find codec for video stream from %s", mPath.c_str());
return -1; return -1;
} }
}
Debug(1, "Video Found decoder %s", mVideoCodec->name); Debug(1, "Video Found decoder %s", mVideoCodec->name);
zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0);
@ -466,7 +457,7 @@ int FfmpegCamera::OpenFfmpeg() {
mVideoCodec->name, av_hwdevice_get_type_name(type)); mVideoCodec->name, av_hwdevice_get_type_name(type));
return -1; return -1;
} }
if ( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX ) if ( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
&& (config->device_type == type) && (config->device_type == type)
) { ) {
hw_pix_fmt = config->pix_fmt; hw_pix_fmt = config->pix_fmt;
@ -476,13 +467,17 @@ int FfmpegCamera::OpenFfmpeg() {
#else #else
hw_pix_fmt = find_fmt_by_hw_type(type); hw_pix_fmt = find_fmt_by_hw_type(type);
#endif #endif
Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt)); 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() : "")); Debug(1, "Creating hwdevice for %s",
if ((ret = av_hwdevice_ctx_create(&hw_device_ctx, type, (hwaccel_device != "" ? hwaccel_device.c_str() : ""));
(hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0)) < 0) { 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."); Error("Failed to create specified HW device.");
return -1; return -1;
} }
@ -503,7 +498,7 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
#endif #endif
e = NULL; e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key); Warning("Option %s not recognized by ffmpeg", e->key);
} }
if ( ret < 0 ) { if ( ret < 0 ) {
Error("Unable to open codec for video stream from %s", mPath.c_str()); Error("Unable to open codec for video stream from %s", mPath.c_str());
@ -530,23 +525,23 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
} else { } else {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
mAudioCodecContext = avcodec_alloc_context3(mAudioCodec); mAudioCodecContext = avcodec_alloc_context3(mAudioCodec);
avcodec_parameters_to_context( mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar ); avcodec_parameters_to_context(
mAudioCodecContext,
mFormatContext->streams[mAudioStreamId]->codecpar
);
#else #else
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec; mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
// = avcodec_alloc_context3(mAudioCodec); // = avcodec_alloc_context3(mAudioCodec);
#endif #endif
Debug(1, "Audio Found decoder");
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0); zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
// Open the codec // Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug(1, "Calling avcodec_open");
if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) { if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) {
#else #else
Debug(1, "Calling avcodec_open2" );
if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) { if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) {
#endif #endif
Error("Unable to open codec for audio stream from %s", mPath.c_str() ); Error("Unable to open codec for audio stream from %s", mPath.c_str());
return -1; return -1;
} }
zm_dump_codec(mAudioCodecContext); zm_dump_codec(mAudioCodecContext);
@ -564,23 +559,18 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
return -1; return -1;
} }
Debug( 3, "Allocated frames");
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
#else #else
int pSize = avpicture_get_size( imagePixFormat, width, height ); int pSize = avpicture_get_size(imagePixFormat, width, height);
#endif #endif
if ( (unsigned int)pSize != imagesize ) { if ( (unsigned int)pSize != imagesize ) {
Error("Image size mismatch. Required: %d Available: %d",pSize,imagesize); Error("Image size mismatch. Required: %d Available: %d", pSize, imagesize);
return -1; return -1;
} }
Debug(4, "Validated imagesize");
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
Debug(1, "Calling sws_isSupportedInput");
if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) { if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
Error("swscale does not support the codec format: %c%c%c%c", Error("swscale does not support the codec format: %c%c%c%c",
(mVideoCodecContext->pix_fmt)&0xff, (mVideoCodecContext->pix_fmt)&0xff,
@ -602,7 +592,7 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
} }
# if 0 # if 0
// Have to get a frame first to find out the actual format returned by decoding // Must get a frame first to find out the actual format returned by decoding
mConvertContext = sws_getContext( mConvertContext = sws_getContext(
mVideoCodecContext->width, mVideoCodecContext->width,
mVideoCodecContext->height, mVideoCodecContext->height,
@ -611,12 +601,13 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
imagePixFormat, SWS_BICUBIC, NULL, imagePixFormat, SWS_BICUBIC, NULL,
NULL, NULL); NULL, NULL);
if ( mConvertContext == NULL ) { if ( mConvertContext == NULL ) {
Error( "Unable to create conversion context for %s", mPath.c_str() ); Error("Unable to create conversion context for %s", mPath.c_str());
return -1; return -1;
} }
#endif #endif
#else // HAVE_LIBSWSCALE #else // HAVE_LIBSWSCALE
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); Fatal("You must compile ffmpeg with the --enable-swscale "
"option to use ffmpeg cameras");
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
if ( if (
@ -625,8 +616,7 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
((unsigned int)mVideoCodecContext->height != height) ((unsigned int)mVideoCodecContext->height != height)
) { ) {
Warning("Monitor dimensions are %dx%d but camera is sending %dx%d", Warning("Monitor dimensions are %dx%d but camera is sending %dx%d",
width, height, mVideoCodecContext->width, mVideoCodecContext->height width, height, mVideoCodecContext->width, mVideoCodecContext->height);
);
} }
mCanCapture = true; mCanCapture = true;
@ -635,7 +625,6 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt
} // int FfmpegCamera::OpenFfmpeg() } // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::Close() { int FfmpegCamera::Close() {
Debug(2, "CloseFfmpeg called."); Debug(2, "CloseFfmpeg called.");
mCanCapture = false; mCanCapture = false;
@ -663,9 +652,8 @@ int FfmpegCamera::Close() {
if ( mVideoCodecContext ) { if ( mVideoCodecContext ) {
avcodec_close(mVideoCodecContext); avcodec_close(mVideoCodecContext);
Debug(1,"After codec close");
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
//avcodec_free_context(&mVideoCodecContext); // avcodec_free_context(&mVideoCodecContext);
#endif #endif
mVideoCodecContext = NULL; // Freed by av_close_input_file mVideoCodecContext = NULL; // Freed by av_close_input_file
} }
@ -694,8 +682,12 @@ int FfmpegCamera::Close() {
return 0; return 0;
} // end FfmpegCamera::Close } // end FfmpegCamera::Close
//Function to handle capture and store // Function to handle capture and store
int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) { int FfmpegCamera::CaptureAndRecord(
Image &image,
timeval recording,
char* event_file
) {
if ( !mCanCapture ) { if ( !mCanCapture ) {
return -1; return -1;
} }
@ -709,7 +701,10 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
if ( ret < 0 ) { if ( ret < 0 ) {
if ( if (
// Check if EOF. // Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || (
(ret == AVERROR_EOF) ||
(mFormatContext->pb && mFormatContext->pb->eof_reached)
) ||
// Check for Connection failure. // Check for Connection failure.
(ret == -110) (ret == -110)
) { ) {
@ -723,9 +718,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
} }
if ( (packet.pts != AV_NOPTS_VALUE) && (packet.pts < -100000) ) { if ( (packet.pts != AV_NOPTS_VALUE) && (packet.pts < -100000) ) {
// Ignore packets that have crazy negative pts. They aren't supposed to happen. // Ignore packets that have crazy negative pts.
Warning("Ignore packet because pts %" PRId64 " is massively negative. Error count is %d", packet.pts, error_count); // They aren't supposed to happen.
dumpPacket(mFormatContext->streams[packet.stream_index], &packet,"Ignored packet"); Warning("Ignore packet because pts %" PRId64 " is massively negative."
" Error count is %d", packet.pts, error_count);
dumpPacket(
mFormatContext->streams[packet.stream_index],
&packet,
"Ignored packet");
if ( error_count > 100 ) { if ( error_count > 100 ) {
Error("Bad packet count over 100, going to close and re-open stream"); Error("Bad packet count over 100, going to close and re-open stream");
return -1; return -1;
@ -738,14 +738,16 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
int keyframe = packet.flags & AV_PKT_FLAG_KEY; int keyframe = packet.flags & AV_PKT_FLAG_KEY;
bytes += packet.size; bytes += packet.size;
dumpPacket(mFormatContext->streams[packet.stream_index], &packet, "Captured Packet"); dumpPacket(
mFormatContext->streams[packet.stream_index],
&packet,
"Captured Packet");
if ( packet.dts == AV_NOPTS_VALUE ) { if ( packet.dts == AV_NOPTS_VALUE ) {
packet.dts = packet.pts; packet.dts = packet.pts;
} }
// Video recording // Video recording
if ( recording.tv_sec ) { if ( recording.tv_sec ) {
uint32_t last_event_id = monitor->GetLastEventId(); uint32_t last_event_id = monitor->GetLastEventId();
uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); uint32_t video_writer_event_id = monitor->GetVideoWriterEventId();
@ -756,12 +758,13 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
if ( videoStore ) { if ( videoStore ) {
Info("Re-starting video storage module"); Info("Re-starting video storage module");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it. // I don't know if this is important or not... but I figure we might
// as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio. // Also don't know how much it matters for audio.
if ( packet.stream_index == mVideoStreamId ) { if ( packet.stream_index == mVideoStreamId ) {
//Write the packet to our video store // Write the packet to our video store
int ret = videoStore->writeVideoFramePacket(&packet); int ret = videoStore->writeVideoFramePacket(&packet);
if ( ret < 0 ) { //Less than zero and we skipped a frame if ( ret < 0 ) { // Less than zero and we skipped a frame
Warning("Error writing last packet to videostore."); Warning("Error writing last packet to videostore.");
} }
} // end if video } // end if video
@ -774,8 +777,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
} // end if videoStore } // end if videoStore
} // end if end of recording } // end if end of recording
if ( last_event_id and !videoStore ) { if ( last_event_id && !videoStore ) {
//Instantiate the video storage module // Instantiate the video storage module
packetqueue->dumpQueue(); packetqueue->dumpQueue();
if ( record_audio ) { if ( record_audio ) {
@ -784,7 +787,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
videoStore = new VideoStore((const char *) event_file, "mp4", videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId], mFormatContext->streams[mVideoStreamId],
NULL, NULL,
startTime,
this->getMonitor()); this->getMonitor());
} else { } else {
@ -792,7 +794,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
videoStore = new VideoStore((const char *) event_file, "mp4", videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId], mFormatContext->streams[mVideoStreamId],
mFormatContext->streams[mAudioStreamId], mFormatContext->streams[mAudioStreamId],
startTime,
this->getMonitor()); this->getMonitor());
} }
} else { } else {
@ -802,39 +803,44 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
videoStore = new VideoStore((const char *) event_file, "mp4", videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId], mFormatContext->streams[mVideoStreamId],
NULL, NULL,
startTime,
this->getMonitor()); this->getMonitor());
} // end if record_audio } // end if record_audio
if ( ! videoStore->open() ) { if ( !videoStore->open() ) {
delete videoStore; delete videoStore;
videoStore = NULL; videoStore = NULL;
} else { } else {
monitor->SetVideoWriterEventId( last_event_id ); monitor->SetVideoWriterEventId(last_event_id);
// Need to write out all the frames from the last keyframe? // Need to write out all the frames from the last keyframe?
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe. // No... need to write out all frames from when the event began.
// Due to PreEventFrames, this could be more than
// since the last keyframe.
unsigned int packet_count = 0; unsigned int packet_count = 0;
ZMPacket *queued_packet; ZMPacket *queued_packet;
// Clear all packets that predate the moment when the recording began // Clear all packets that predate the moment when the recording began
packetqueue->clear_unwanted_packets(&recording, mVideoStreamId); packetqueue->clear_unwanted_packets(
&recording, monitor->GetPreEventCount(), mVideoStreamId);
while ( ( queued_packet = packetqueue->popPacket() ) ) { while ( (queued_packet = packetqueue->popPacket()) ) {
AVPacket *avp = queued_packet->av_packet(); AVPacket *avp = queued_packet->av_packet();
packet_count += 1; packet_count += 1;
//Write the packet to our video store // Write the packet to our video store
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)",
avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue->size()); avp->stream_index,
avp->flags & AV_PKT_FLAG_KEY,
packetqueue->size());
if ( avp->stream_index == mVideoStreamId ) { if ( avp->stream_index == mVideoStreamId ) {
ret = videoStore->writeVideoFramePacket(avp); ret = videoStore->writeVideoFramePacket(avp);
have_video_keyframe = true; have_video_keyframe = true;
} else if ( avp->stream_index == mAudioStreamId ) { } else if ( avp->stream_index == mAudioStreamId ) {
ret = videoStore->writeAudioFramePacket(avp); ret = videoStore->writeAudioFramePacket(avp);
} else { } else {
Warning("Unknown stream id in queued packet (%d)", avp->stream_index); Warning("Unknown stream id in queued packet (%d)",
avp->stream_index);
ret = -1; ret = -1;
} }
if ( ret < 0 ) { if ( ret < 0 ) {
@ -850,7 +856,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
// Not recording // Not recording
if ( videoStore ) { if ( videoStore ) {
Debug(1,"Deleting videoStore instance"); Debug(1, "Deleting videoStore instance");
delete videoStore; delete videoStore;
videoStore = NULL; videoStore = NULL;
have_video_keyframe = false; have_video_keyframe = false;
@ -863,11 +869,17 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
if ( packet.stream_index == mVideoStreamId ) { if ( packet.stream_index == mVideoStreamId ) {
if ( keyframe ) { if ( keyframe ) {
Debug(3, "Clearing queue"); Debug(3, "Clearing queue");
if ( packetqueue->packet_count(mVideoStreamId) >= monitor->GetImageBufferCount() ) { if (
Warning("ImageBufferCount %d is too small. Needs to be at least %d. Either increase it or decrease time between keyframes", packetqueue->packet_count(mVideoStreamId)
>=
monitor->GetImageBufferCount()
) {
Warning(
"ImageBufferCount %d is too small. "
"Needs to be at least %d. "
"Either increase it or decrease time between keyframes",
monitor->GetImageBufferCount(), monitor->GetImageBufferCount(),
packetqueue->packet_count(mVideoStreamId)+1 packetqueue->packet_count(mVideoStreamId)+1);
);
} }
packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId); packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
@ -877,34 +889,27 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
packetqueue->queuePacket(&packet); packetqueue->queuePacket(&packet);
} }
} else if ( packet.stream_index == mAudioStreamId ) { } else if ( packet.stream_index == mAudioStreamId ) {
// The following lines should ensure that the queue always begins with a video keyframe // Ensure that the queue always begins with a video keyframe
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
if ( record_audio && packetqueue->size() ) { if ( record_audio && packetqueue->size() ) {
// if it's audio, and we are doing audio, and there is already something in the queue
packetqueue->queuePacket(&packet); packetqueue->queuePacket(&packet);
} }
} // end if packet type } // end if packet type
if ( packet.stream_index == mVideoStreamId ) { if ( packet.stream_index == mVideoStreamId ) {
// only do decode if we have had a keyframe, should save a few cycles. if ( (have_video_keyframe || keyframe) && videoStore ) {
if ( have_video_keyframe || keyframe ) {
if ( videoStore ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket(&packet); int ret = videoStore->writeVideoFramePacket(&packet);
if ( ret < 0 ) { //Less than zero and we skipped a frame if ( ret < 0 ) {
zm_av_packet_unref(&packet); // Less than zero and we skipped a frame
return 0; Error("Unable to write video packet %d: %s",
} frameCount, av_make_error_string(ret).c_str());
} else {
have_video_keyframe = true; have_video_keyframe = true;
} }
} // end if keyframe or have_video_keyframe } // end if keyframe or have_video_keyframe
Debug(4, "about to decode video");
ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet); ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet);
if ( ret < 0 ) { if ( ret < 0 ) {
Warning("Unable to receive frame %d: %s, continuing. error count is %s", Warning("Unable to receive frame %d: %s. error count is %d",
frameCount, av_make_error_string(ret).c_str(), error_count); frameCount, av_make_error_string(ret).c_str(), error_count);
error_count += 1; error_count += 1;
if ( error_count > 100 ) { if ( error_count > 100 ) {
@ -914,10 +919,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
zm_av_packet_unref(&packet); zm_av_packet_unref(&packet);
continue; continue;
} }
if ( error_count > 0 ) error_count --; if ( error_count > 0 ) error_count--;
zm_dump_video_frame(mRawFrame); zm_dump_video_frame(mRawFrame);
#if HAVE_LIBAVUTIL_HWCONTEXT_H #if HAVE_LIBAVUTIL_HWCONTEXT_H
if ( (hw_pix_fmt != AV_PIX_FMT_NONE) && (mRawFrame->format == hw_pix_fmt) ) { if (
(hw_pix_fmt != AV_PIX_FMT_NONE)
&&
(mRawFrame->format == hw_pix_fmt)
) {
/* retrieve data from GPU to CPU */ /* retrieve data from GPU to CPU */
ret = av_hwframe_transfer_data(hwFrame, mRawFrame, 0); ret = av_hwframe_transfer_data(hwFrame, mRawFrame, 0);
if ( ret < 0 ) { if ( ret < 0 ) {
@ -945,23 +954,23 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
frameComplete = 1; frameComplete = 1;
frameCount++; frameCount++;
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams } else if ( packet.stream_index == mAudioStreamId ) {
// FIXME best way to copy all other streams
frameComplete = 1; frameComplete = 1;
if ( videoStore ) { if ( videoStore ) {
if ( record_audio ) { if ( record_audio ) {
if ( have_video_keyframe ) { if ( have_video_keyframe ) {
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", // Write the packet to our video store
mAudioStreamId, packet.stream_index); // FIXME no relevance of last key frame
//Write the packet to our video store
//FIXME no relevance of last key frame
int ret = videoStore->writeAudioFramePacket(&packet); int ret = videoStore->writeAudioFramePacket(&packet);
if ( ret < 0 ) {//Less than zero and we skipped a frame if ( ret < 0 ) {
// Less than zero and we skipped a frame
Warning("Failure to write audio packet."); Warning("Failure to write audio packet.");
zm_av_packet_unref(&packet); zm_av_packet_unref(&packet);
return 0; return 0;
} }
} else { } else {
Debug(3, "Not recording audio yet because we don't have a video keyframe yet"); Debug(3, "Not recording audio because no video keyframe");
} }
} else { } else {
Debug(4, "Not doing recording of audio packet"); Debug(4, "Not doing recording of audio packet");
@ -975,20 +984,27 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0) #if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0)
Debug(3, "Some other stream index %d, %s", Debug(3, "Some other stream index %d, %s",
packet.stream_index, packet.stream_index,
av_get_media_type_string(mFormatContext->streams[packet.stream_index]->codecpar->codec_type) av_get_media_type_string(
mFormatContext->streams[packet.stream_index]->codecpar->codec_type)
); );
#else #else
Debug(3, "Some other stream index %d", packet.stream_index); Debug(3, "Some other stream index %d", packet.stream_index);
#endif #endif
} // end if is video or audio or something else } // end if is video or audio or something else
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version. // the packet contents are ref counted... when queuing, we allocate another
// packet and reference it with that one, so we should always need to unref
// here, which should not affect the queued version.
zm_av_packet_unref(&packet); zm_av_packet_unref(&packet);
} // end while ! frameComplete } // end while ! frameComplete
return frameCount; return frameCount;
} // end FfmpegCamera::CaptureAndRecord } // end FfmpegCamera::CaptureAndRecord
int FfmpegCamera::transfer_to_image(Image &image, AVFrame *output_frame, AVFrame *input_frame) { int FfmpegCamera::transfer_to_image(
Image &image,
AVFrame *output_frame,
AVFrame *input_frame
) {
uint8_t* directbuffer; uint8_t* directbuffer;
/* Request a writeable buffer of the target image */ /* Request a writeable buffer of the target image */
@ -1023,9 +1039,11 @@ int FfmpegCamera::transfer_to_image(Image &image, AVFrame *output_frame, AVFrame
} }
} }
if ( sws_scale(mConvertContext, input_frame->data, input_frame->linesize, if ( sws_scale(
0, mVideoCodecContext->height, output_frame->data, output_frame->linesize) <= 0 ) { mConvertContext, input_frame->data, input_frame->linesize,
Error("Unable to convert raw format %u to target format %u at frame %d codec %u ", 0, mVideoCodecContext->height,
output_frame->data, output_frame->linesize) <= 0 ) {
Error("Unable to convert format %u to format %u at frame %d codec %u",
input_frame->format, input_frame->format,
imagePixFormat, frameCount, imagePixFormat, frameCount,
mVideoCodecContext->pix_fmt mVideoCodecContext->pix_fmt
@ -1033,14 +1051,15 @@ int FfmpegCamera::transfer_to_image(Image &image, AVFrame *output_frame, AVFrame
return -1; return -1;
} }
#else // HAVE_LIBSWSCALE #else // HAVE_LIBSWSCALE
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); Fatal("You must compile ffmpeg with the --enable-swscale "
"option to use ffmpeg cameras");
#endif // HAVE_LIBSWSCALE #endif // HAVE_LIBSWSCALE
return 0; return 0;
} // end int FfmpegCamera::transfer_to_image(Image &i, AVFrame *output_frame, AVFrame input_frame) } // end int FfmpegCamera::transfer_to_image
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) { int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
//FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx); // FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
//Debug(4, "FfmpegInterruptCallback"); // Debug(4, "FfmpegInterruptCallback");
return zm_terminate; return zm_terminate;
} }

View File

@ -84,7 +84,6 @@ class FfmpegCamera : public Camera {
struct SwsContext *mConvertContext; struct SwsContext *mConvertContext;
#endif #endif
int64_t startTime;
int error_count; int error_count;
public: public:

View File

@ -1,14 +1,30 @@
//
// ZoneMinder Fifo Debug
// Copyright (C) 2019 ZoneMinder LLC
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h>
#include <sys/file.h> #include <sys/file.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <signal.h> #include <signal.h>
#include "zm.h" #include "zm.h"
#include "zm_db.h"
#include "zm_time.h" #include "zm_time.h"
#include "zm_mpeg.h"
#include "zm_signal.h" #include "zm_signal.h"
#include "zm_monitor.h" #include "zm_monitor.h"
#include "zm_fifo.h" #include "zm_fifo.h"
@ -17,229 +33,229 @@ static bool zm_fifodbg_inited = false;
FILE *zm_fifodbg_log_fd = 0; FILE *zm_fifodbg_log_fd = 0;
char zm_fifodbg_log[PATH_MAX] = ""; char zm_fifodbg_log[PATH_MAX] = "";
static bool zmFifoDbgOpen(){ static bool zmFifoDbgOpen() {
if (zm_fifodbg_log_fd) if ( zm_fifodbg_log_fd )
fclose(zm_fifodbg_log_fd); fclose(zm_fifodbg_log_fd);
zm_fifodbg_log_fd = NULL; zm_fifodbg_log_fd = NULL;
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
FifoStream::fifo_create_if_missing(zm_fifodbg_log); FifoStream::fifo_create_if_missing(zm_fifodbg_log);
int fd = open(zm_fifodbg_log,O_WRONLY|O_NONBLOCK|O_TRUNC); int fd = open(zm_fifodbg_log, O_WRONLY|O_NONBLOCK|O_TRUNC);
if (fd < 0) if ( fd < 0 )
return ( false ); return false;
int res = flock(fd,LOCK_EX | LOCK_NB); int res = flock(fd, LOCK_EX | LOCK_NB);
if (res < 0) if ( res < 0 ) {
{
close(fd); close(fd);
return ( false ); return false;
} }
zm_fifodbg_log_fd = fdopen(fd,"wb"); zm_fifodbg_log_fd = fdopen(fd, "wb");
if (zm_fifodbg_log_fd == NULL) if ( zm_fifodbg_log_fd == NULL ) {
{
close(fd); close(fd);
return ( false ); return false;
} }
return ( true ); return true;
} }
int zmFifoDbgInit(Monitor *monitor){
int zmFifoDbgInit(Monitor *monitor) {
zm_fifodbg_inited = true; zm_fifodbg_inited = true;
snprintf( zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log", monitor->getStorage()->Path(), monitor->Id() ); snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log",
monitor->getStorage()->Path(), monitor->Id());
zmFifoDbgOpen(); zmFifoDbgOpen();
return 1; return 1;
} }
void zmFifoDbgOutput( int hex, const char * const file, const int line, const int level, const char *fstring, ... ){
void zmFifoDbgOutput(
int hex,
const char * const file,
const int line,
const int level,
const char *fstring,
...
) {
char dbg_string[8192]; char dbg_string[8192];
int str_size = sizeof(dbg_string);
va_list arg_ptr; va_list arg_ptr;
if (! zm_fifodbg_inited) if ( (!zm_fifodbg_inited) || ( !zm_fifodbg_log_fd && !zmFifoDbgOpen() ) )
return;
if (! zm_fifodbg_log_fd && ! zmFifoDbgOpen())
return; return;
char *dbg_ptr = dbg_string; char *dbg_ptr = dbg_string;
va_start( arg_ptr, fstring ); va_start(arg_ptr, fstring);
if ( hex ) if ( hex ) {
{ unsigned char *data = va_arg(arg_ptr, unsigned char *);
unsigned char *data = va_arg( arg_ptr, unsigned char * ); int len = va_arg(arg_ptr, int);
int len = va_arg( arg_ptr, int ); dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), "%d:", len);
int i; for ( int i = 0; i < len; i++ ) {
dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), "%d:", len ); dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), " %02x", data[i]);
for ( i = 0; i < len; i++ )
{
dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), " %02x", data[i] );
} }
} } else {
else dbg_ptr += vsnprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), fstring, arg_ptr);
{
dbg_ptr += vsnprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), fstring, arg_ptr );
} }
va_end(arg_ptr); va_end(arg_ptr);
strncpy( dbg_ptr++, "\n", 1); strncpy(dbg_ptr++, "\n", 2);
int res = fwrite( dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd ); int res = fwrite(dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd);
if (res != 1){ if ( res != 1 ) {
fclose(zm_fifodbg_log_fd); fclose(zm_fifodbg_log_fd);
zm_fifodbg_log_fd = NULL; zm_fifodbg_log_fd = NULL;
} } else {
else
{
fflush(zm_fifodbg_log_fd); fflush(zm_fifodbg_log_fd);
} }
} }
bool FifoStream::sendRAWFrames(){
bool FifoStream::sendRAWFrames() {
static unsigned char buffer[RAW_BUFFER]; static unsigned char buffer[RAW_BUFFER];
int fd = open(stream_path,O_RDONLY); int fd = open(stream_path, O_RDONLY);
if (fd < 0) if ( fd < 0 ) {
{ Error("Can't open %s: %s", stream_path, strerror(errno));
Error( "Can't open %s: %s", stream_path, strerror(errno)); return false;
return( false );
} }
while( (bytes_read = read(fd,buffer,RAW_BUFFER)) ) while ( (bytes_read = read(fd, buffer, RAW_BUFFER)) ) {
{ if ( bytes_read == 0 )
if (bytes_read == 0)
continue; continue;
if (bytes_read < 0) if ( bytes_read < 0 ) {
{ Error("Problem during reading: %s", strerror(errno));
Error( "Problem during reading: %s", strerror(errno));
close(fd); close(fd);
return( false ); return false;
} }
if ( fwrite( buffer, bytes_read, 1, stdout ) != 1){ if ( fwrite(buffer, bytes_read, 1, stdout) != 1 ) {
Error( "Problem during writing: %s", strerror(errno)); Error("Problem during writing: %s", strerror(errno));
close(fd); close(fd);
return( false ); return false;
} }
fflush( stdout ); fflush(stdout);
} }
close(fd); close(fd);
return ( true ); return true;
} }
void FifoStream::file_create_if_missing(const char * path, bool is_fifo,bool delete_fake_fifo){ void FifoStream::file_create_if_missing(
const char * path,
bool is_fifo,
bool delete_fake_fifo
) {
static struct stat st; static struct stat st;
if(stat(path,&st) == 0){ if ( stat(path, &st) == 0 ) {
if ( (!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo )
if (! is_fifo || S_ISFIFO(st.st_mode) || ! delete_fake_fifo)
return; return;
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path); Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path);
unlink(path); unlink(path);
} }
int fd; int fd;
if (! is_fifo){ if ( !is_fifo ) {
Debug(5, "Creating non fifo file as requested: %s", path); Debug(5, "Creating non fifo file as requested: %s", path);
fd = open(path,O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR); fd = open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
close(fd); close(fd);
return; return;
} }
Debug(5, "Making fifo file of: %s", path); Debug(5, "Making fifo file of: %s", path);
mkfifo(path,S_IRUSR|S_IWUSR); mkfifo(path, S_IRUSR|S_IWUSR);
} }
void FifoStream::fifo_create_if_missing(const char * path, bool delete_fake_fifo){
file_create_if_missing(path,true,delete_fake_fifo); void FifoStream::fifo_create_if_missing(
const char * path,
bool delete_fake_fifo
) {
file_create_if_missing(path, true, delete_fake_fifo);
} }
bool FifoStream::sendMJEGFrames(){
bool FifoStream::sendMJEGFrames() {
static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
int fd = open(stream_path,O_RDONLY); int fd = open(stream_path, O_RDONLY);
if (fd < 0) if ( fd < 0 ) {
{ Error("Can't open %s: %s", stream_path, strerror(errno));
Error( "Can't open %s: %s", stream_path, strerror(errno)); return false;
return( false );
} }
total_read = 0; total_read = 0;
while( (bytes_read = read(fd,buffer+total_read,ZM_MAX_IMAGE_SIZE-total_read)) ) while (
{ (bytes_read = read(fd, buffer+total_read, ZM_MAX_IMAGE_SIZE-total_read))
if (bytes_read < 0) ) {
{ if ( bytes_read < 0 ) {
Error( "Problem during reading: %s", strerror(errno)); Error("Problem during reading: %s", strerror(errno));
close(fd); close(fd);
return( false ); return false;
} }
total_read+=bytes_read; total_read += bytes_read;
} }
close(fd); close(fd);
if (total_read == 0)
return( true ); if ( (total_read == 0) || (frame_count%frame_mod != 0) )
if (frame_count%frame_mod != 0) return true;
return (true );
if (fprintf( stdout, "--ZoneMinderFrame\r\n" ) < 0 ) if ( fprintf(stdout,
{ "--ZoneMinderFrame\r\n"
Error( "Problem during writing: %s", strerror(errno)); "Content-Type: image/jpeg\r\n"
return( false ); "Content-Length: %d\r\n\r\n",
total_read) < 0 ) {
Error("Problem during writing: %s", strerror(errno));
return false;
} }
fprintf( stdout, "Content-Type: image/jpeg\r\n" ); if ( fwrite(buffer, total_read, 1, stdout) != 1 ) {
fprintf( stdout, "Content-Length: %d\r\n\r\n", total_read ); Error("Problem during reading: %s", strerror(errno));
if ( fwrite( buffer, total_read, 1, stdout ) != 1){ return false;
Error( "Problem during reading: %s", strerror(errno));
return( false );
} }
fprintf( stdout, "\r\n\r\n" ); fprintf(stdout, "\r\n\r\n");
fflush( stdout); fflush(stdout);
last_frame_sent = TV_2_FLOAT( now ); last_frame_sent = TV_2_FLOAT(now);
frame_count++; frame_count++;
return( true ); return true;
} }
void FifoStream::setStreamStart( const char * path ){ void FifoStream::setStreamStart(const char * path) {
stream_path = strdup(path); stream_path = strdup(path);
} }
void FifoStream::setStreamStart( int monitor_id, const char * format ){
void FifoStream::setStreamStart(int monitor_id, const char * format) {
char diag_path[PATH_MAX]; char diag_path[PATH_MAX];
const char * filename; const char * filename;
Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
if (! strcmp(format,"reference") ) if ( !strcmp(format, "reference") ) {
{
stream_type = MJPEG; stream_type = MJPEG;
filename = "diagpipe-r.jpg"; filename = "diagpipe-r.jpg";
} } else if ( !strcmp(format, "delta") ) {
else if (! strcmp(format,"delta"))
{
filename = "diagpipe-d.jpg"; filename = "diagpipe-d.jpg";
stream_type = MJPEG; stream_type = MJPEG;
} } else {
else
{
stream_type = RAW; stream_type = RAW;
filename = "dbgpipe.log"; filename = "dbgpipe.log";
} }
snprintf( diag_path, sizeof(diag_path), "%s/%d/%s", monitor->getStorage()->Path(), monitor->Id(), filename ); snprintf(diag_path, sizeof(diag_path), "%s/%d/%s",
monitor->getStorage()->Path(), monitor->Id(), filename);
setStreamStart(diag_path); setStreamStart(diag_path);
} }
void FifoStream::runStream(){
if (stream_type == MJPEG) void FifoStream::runStream() {
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); if ( stream_type == MJPEG ) {
else fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n");
fprintf( stdout, "Content-Type: text/html\r\n\r\n" ); } else {
fprintf(stdout, "Content-Type: text/html\r\n\r\n");
}
char lock_file[PATH_MAX]; char lock_file[PATH_MAX];
strcpy(lock_file,stream_path); snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path);
strcat(lock_file,".rlock"); file_create_if_missing(lock_file, false);
file_create_if_missing(lock_file,false);
int fd_lock = open(lock_file,O_RDONLY); int fd_lock = open(lock_file, O_RDONLY);
if (fd_lock < 0) if ( fd_lock < 0 ) {
{ Error("Can't open %s: %s", lock_file, strerror(errno));
Error( "Can't open %s: %s", lock_file, strerror(errno));
return; return;
} }
int res = flock(fd_lock,LOCK_EX | LOCK_NB); int res = flock(fd_lock, LOCK_EX | LOCK_NB);
if (res < 0) if ( res < 0 ) {
{ Error("Flocking problem on %s: - %s", lock_file, strerror(errno));
Error( "Flocking problem on %s: - %s", lock_file, strerror(errno) );
close(fd_lock); close(fd_lock);
return; return;
} }
while( !zm_terminate ) while ( !zm_terminate ) {
{ gettimeofday(&now, NULL);
gettimeofday( &now, NULL );
checkCommandQueue(); checkCommandQueue();
if (stream_type == MJPEG) if ( stream_type == MJPEG ) {
{ if ( !sendMJEGFrames() )
if (! sendMJEGFrames())
zm_terminate = true; zm_terminate = true;
} } else {
else if ( !sendRAWFrames() )
{
if (! sendRAWFrames())
zm_terminate = true; zm_terminate = true;
} }
} }

View File

@ -1,6 +1,25 @@
//
// ZoneMinder Fifo Debug
// Copyright (C) 2019 ZoneMinder LLC
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#ifndef ZM_FIFO_H #ifndef ZM_FIFO_H
#define ZM_FIFO_H #define ZM_FIFO_H
#if 0
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -13,49 +32,55 @@
#include "zm.h" #include "zm.h"
#include "zm_image.h" #include "zm_image.h"
#endif
#include "zm_monitor.h" #include "zm_monitor.h"
#include "zm_stream.h" #include "zm_stream.h"
#define zmFifoDbgPrintf(level, params...) {\
#define zmFifoDbgPrintf(level,params...) {\ zmFifoDbgOutput(0, __FILE__, __LINE__, level, ##params);\
zmFifoDbgOutput( 0, __FILE__, __LINE__, level, ##params );\
} }
#ifndef ZM_DBG_OFF #ifndef ZM_DBG_OFF
#define FifoDebug(level,params...) zmFifoDbgPrintf(level,##params) #define FifoDebug(level, params...) zmFifoDbgPrintf(level, ##params)
#else #else
#define FifoDebug(level,params...) #define FifoDebug(level, params...)
#endif #endif
void zmFifoDbgOutput( int hex, const char * const file, const int line, const int level, const char *fstring, ... ) __attribute__ ((format(printf, 5, 6))); void zmFifoDbgOutput(
int hex,
const char * const file,
const int line,
const int level,
const char *fstring,
...) __attribute__((format(printf, 5, 6)));
int zmFifoDbgInit(Monitor * monitor); int zmFifoDbgInit(Monitor * monitor);
class FifoStream : public StreamBase class FifoStream : public StreamBase {
{ private:
private:
char * stream_path; char * stream_path;
int fd; int fd;
int total_read; int total_read;
int bytes_read; int bytes_read;
unsigned int frame_count; unsigned int frame_count;
static void file_create_if_missing(const char * path, bool is_fifo, bool delete_fake_fifo=true); static void file_create_if_missing(
const char * path,
bool is_fifo,
bool delete_fake_fifo = true
);
protected: protected:
typedef enum { MJPEG, RAW } StreamType; typedef enum { MJPEG, RAW } StreamType;
StreamType stream_type; StreamType stream_type;
bool sendMJEGFrames( ); bool sendMJEGFrames();
bool sendRAWFrames( ); bool sendRAWFrames();
void processCommand( const CmdMsg *msg ) {}; void processCommand(const CmdMsg *msg) {}
public:
FifoStream(){
}
static void fifo_create_if_missing(const char * path,bool delete_fake_fifo=true);
void setStreamStart( const char * path );
void setStreamStart( int monitor_id, const char * format );
public:
FifoStream() {}
static void fifo_create_if_missing(
const char * path,
bool delete_fake_fifo = true);
void setStreamStart(const char * path);
void setStreamStart(int monitor_id, const char * format);
void runStream(); void runStream();
}; };
#endif #endif // ZM_FIFO_H

View File

@ -1525,10 +1525,14 @@ bool Monitor::Analyse() {
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
name, image_count, event->Id()); name, image_count, event->Id());
closeEvent(); closeEvent();
} } else {
Debug(3, "pre-alarm-count %d", Event::PreAlarmCount());
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames // This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count) ) { Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d",
Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(),
( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length
);
}
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) {
shared_data->state = state = ALARM; shared_data->state = state = ALARM;
// lets construct alarm cause. It will contain cause + names of zones alarmed // lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause = ""; std::string alarm_cause = "";
@ -1539,7 +1543,7 @@ bool Monitor::Analyse() {
} }
if ( !alarm_cause.empty() ) alarm_cause[0] = ' '; if ( !alarm_cause.empty() ) alarm_cause[0] = ' ';
alarm_cause = cause + alarm_cause; alarm_cause = cause + alarm_cause;
strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1); strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s", Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s",
name, image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause); name, image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause);
@ -1550,7 +1554,9 @@ bool Monitor::Analyse() {
if ( analysis_fps && pre_event_count ) { if ( analysis_fps && pre_event_count ) {
// If analysis fps is set, // If analysis fps is set,
// compute the index for pre event images in the dedicated buffer // compute the index for pre event images in the dedicated buffer
pre_index = pre_event_buffer_count ? image_count%pre_event_buffer_count : 0; pre_index = pre_event_buffer_count ? image_count % pre_event_buffer_count : 0;
Debug(3, "pre-index %d = image_count(%d) %% pre_event_buffer_count(%d)",
pre_index, image_count, pre_event_buffer_count);
// Seek forward the next filled slot in to the buffer (oldest data) // Seek forward the next filled slot in to the buffer (oldest data)
// from the current position // from the current position
@ -1559,6 +1565,8 @@ bool Monitor::Analyse() {
// Slot is empty, removing image from counter // Slot is empty, removing image from counter
pre_event_images--; pre_event_images--;
} }
Debug(3, "pre-index %d, pre-event_images %d",
pre_index, pre_event_images);
event = new Event(this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap); event = new Event(this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap);
} else { } else {
@ -1569,7 +1577,7 @@ bool Monitor::Analyse() {
else else
pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count;
Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d)", Debug(3, "Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d)",
pre_index, index, image_buffer_count, pre_event_count); pre_index, index, image_buffer_count, pre_event_count);
// Seek forward the next filled slot in to the buffer (oldest data) // Seek forward the next filled slot in to the buffer (oldest data)
@ -1624,7 +1632,10 @@ bool Monitor::Analyse() {
Info("%s: %03d - Gone into alert state", name, image_count); Info("%s: %03d - Gone into alert state", name, image_count);
shared_data->state = state = ALERT; shared_data->state = state = ALERT;
} else if ( state == ALERT ) { } else if ( state == ALERT ) {
if ( image_count-last_alarm_count > post_event_count ) { if (
( image_count-last_alarm_count > post_event_count )
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length )
) {
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images", Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
name, image_count, event->Id(), event->Frames(), event->AlarmFrames()); name, image_count, event->Id(), event->Frames(), event->AlarmFrames());
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )

View File

@ -157,12 +157,12 @@ unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_
AVPacket *av_packet = &(zm_packet->packet); AVPacket *av_packet = &(zm_packet->packet);
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep);
// Want frames_to_keep video keyframes. Otherwise, we may not have enough // Want frames_to_keep video keyframes. Otherwise, we may not have enough
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) { if ( (av_packet->stream_index == stream_id) && (av_packet->flags & AV_PKT_FLAG_KEY) ) {
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep ); av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep);
break; break;
} }
} }
@ -210,25 +210,72 @@ int zm_packetqueue::packet_count( int stream_id ) {
return packet_counts[stream_id]; return packet_counts[stream_id];
} // end int zm_packetqueue::packet_count( int stream_id ) } // end int zm_packetqueue::packet_count( int stream_id )
void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId ) { // Clear packets before the given timestamp.
// Must also take into account pre_event_count frames
void zm_packetqueue::clear_unwanted_packets(
timeval *recording_started,
int pre_event_count,
int mVideoStreamId) {
// Need to find the keyframe <= recording_started. Can get rid of audio packets. // Need to find the keyframe <= recording_started. Can get rid of audio packets.
if ( pktQueue.empty() ) if ( pktQueue.empty() )
return; return;
// Step 1 - find keyframe < recording_started. // Step 1 - find frame <= recording_started.
// Step 2 - pop packets until we get to the packet in step 2 // Step 2 - go back pre_event_count
// Step 3 - find a keyframe
// Step 4 - pop packets until we get to the packet in step 3
std::list<ZMPacket *>::reverse_iterator it; std::list<ZMPacket *>::reverse_iterator it;
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId); // Step 1 - find frame <= recording_started.
Debug(3, "Looking for frame before start recording stream id (%d), queue has %d packets",
mVideoStreamId, pktQueue.size());
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) { for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
if (
( av_packet->stream_index == mVideoStreamId )
&&
timercmp( &(zm_packet->timestamp), recording_started, <= )
) {
Debug(3, "Found frame before start with stream index %d at %d.%d",
av_packet->stream_index,
zm_packet->timestamp.tv_sec,
zm_packet->timestamp.tv_usec);
break;
}
Debug(3, "Not Found frame before start with stream index %d at %d.%d",
av_packet->stream_index,
zm_packet->timestamp.tv_sec,
zm_packet->timestamp.tv_usec);
}
if ( it == pktQueue.rend() ) {
Debug(1, "Didn't find a frame before event starttime. keeping all");
return;
}
Debug(1, "Seeking back %d frames", pre_event_count);
for ( ; pre_event_count && (it != pktQueue.rend()); ++ it ) {
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
if ( av_packet->stream_index == mVideoStreamId ) {
--pre_event_count;
}
}
if ( it == pktQueue.rend() ) {
Debug(1, "ran out of pre_event frames before event starttime. keeping all");
return;
}
Debug(3, "Looking for keyframe");
for ( ; it != pktQueue.rend(); ++ it ) {
ZMPacket *zm_packet = *it; ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet); AVPacket *av_packet = &(zm_packet->packet);
if ( if (
( av_packet->flags & AV_PKT_FLAG_KEY ) ( av_packet->flags & AV_PKT_FLAG_KEY )
&& &&
( av_packet->stream_index == mVideoStreamId ) ( av_packet->stream_index == mVideoStreamId )
&&
timercmp( &(zm_packet->timestamp), recording_started, <= )
) { ) {
Debug(3, "Found keyframe before start with stream index %d at %d.%d", Debug(3, "Found keyframe before start with stream index %d at %d.%d",
av_packet->stream_index, av_packet->stream_index,

View File

@ -43,7 +43,7 @@ public:
void clearQueue(); void clearQueue();
void dumpQueue(); void dumpQueue();
unsigned int size(); unsigned int size();
void clear_unwanted_packets(timeval *recording, int mVideoStreamId); void clear_unwanted_packets(timeval *recording, int pre_event_count, int mVideoStreamId);
int packet_count(int stream_id); int packet_count(int stream_id);
private: private:
std::list<ZMPacket *> pktQueue; std::list<ZMPacket *> pktQueue;

View File

@ -418,7 +418,6 @@ int RemoteCameraRtsp::CaptureAndRecord(Image &image, timeval recording, char* ev
videoStore = new VideoStore((const char *)event_file, "mp4", videoStore = new VideoStore((const char *)event_file, "mp4",
mFormatContext->streams[mVideoStreamId], mFormatContext->streams[mVideoStreamId],
mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId], mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId],
startTime,
this->getMonitor() ); this->getMonitor() );
strcpy(oldDirectory, event_file); strcpy(oldDirectory, event_file);
} // end if ! videoStore } // end if ! videoStore

View File

@ -36,7 +36,6 @@ VideoStore::VideoStore(
const char *format_in, const char *format_in,
AVStream *p_video_in_stream, AVStream *p_video_in_stream,
AVStream *p_audio_in_stream, AVStream *p_audio_in_stream,
int64_t nStartTime,
Monitor *monitor Monitor *monitor
) { ) {
@ -891,12 +890,22 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
// Scale the PTS of the outgoing packet to be the correct time base // Scale the PTS of the outgoing packet to be the correct time base
if ( ipkt->pts != AV_NOPTS_VALUE ) { if ( ipkt->pts != AV_NOPTS_VALUE ) {
// ffmpeg has a bug where it screws up the pts to massively negative.
if ( (!video_first_pts) && (ipkt->pts >= 0) ) { if ( (!video_first_pts) && (ipkt->pts >= 0) ) {
// This is the first packet. // This is the first packet.
opkt.pts = 0; opkt.pts = 0;
Debug(2, "Starting video first_pts will become %" PRId64, ipkt->pts); Debug(2, "Starting video first_pts will become %" PRId64, ipkt->pts);
video_first_pts = ipkt->pts; video_first_pts = ipkt->pts;
#if 1
if ( audio_in_stream ) {
// Since audio starts after the start of the video, need to set this here.
audio_first_pts = av_rescale_q(
ipkt->pts,
video_in_stream->time_base,
audio_in_stream->time_base
);
Debug(2, "Starting audio first_pts will become %" PRId64, audio_first_pts);
}
#endif
} else { } else {
opkt.pts = av_rescale_q( opkt.pts = av_rescale_q(
ipkt->pts - video_first_pts, ipkt->pts - video_first_pts,
@ -924,6 +933,17 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
opkt.dts = 0; opkt.dts = 0;
Debug(1, "Starting video first_dts will become (%" PRId64 ")", ipkt->dts); Debug(1, "Starting video first_dts will become (%" PRId64 ")", ipkt->dts);
video_first_dts = ipkt->dts; video_first_dts = ipkt->dts;
#if 1
if ( audio_in_stream ) {
// Since audio starts after the start of the video, need to set this here.
audio_first_dts = av_rescale_q(
ipkt->dts,
video_in_stream->time_base,
audio_in_stream->time_base
);
Debug(2, "Starting audio first dts will become %" PRId64, audio_first_dts);
}
#endif
} else { } else {
opkt.dts = av_rescale_q( opkt.dts = av_rescale_q(
ipkt->dts - video_first_dts, ipkt->dts - video_first_dts,
@ -1093,7 +1113,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
if ( !audio_first_pts ) { if ( !audio_first_pts ) {
opkt.pts = 0; opkt.pts = 0;
audio_first_pts = ipkt->pts; audio_first_pts = ipkt->pts;
Debug(1, "No video_first_pts"); Debug(1, "No audio_first_pts");
} else { } else {
opkt.pts = av_rescale_q( opkt.pts = av_rescale_q(
ipkt->pts - audio_first_pts, ipkt->pts - audio_first_pts,

View File

@ -85,7 +85,6 @@ public:
const char *format_in, const char *format_in,
AVStream *video_in_stream, AVStream *video_in_stream,
AVStream *audio_in_stream, AVStream *audio_in_stream,
int64_t nStartTime,
Monitor * p_monitor); Monitor * p_monitor);
bool open(); bool open();
~VideoStore(); ~VideoStore();

View File

@ -637,6 +637,7 @@ color:#ffa801;
.container-fluid { .container-fluid {
position: relative; position: relative;
padding-bottom: 10px;
} }
.sidebar { .sidebar {

View File

@ -77,6 +77,11 @@ if ( canView('Control') && $monitor->Type() == 'Local' ) {
</div> </div>
<div id="closeControl"><a href="#" onclick="<?php echo $popup ? 'window.close()' : 'history.go(-1);return false;' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div> <div id="closeControl"><a href="#" onclick="<?php echo $popup ? 'window.close()' : 'history.go(-1);return false;' ?>"><?php echo $popup ? translate('Close') : translate('Back') ?></a></div>
</div> </div>
<?php
if ( $monitor->Status() != 'Capturing' ) {
echo '<div class="warning">Monitor is not capturing. We will be unable to provide an image</div>';
}
?>
<div id="content"> <div id="content">
<div id="imageFeed" <div id="imageFeed"
<?php <?php