From 0686f487acd6c9f62ba93e01b2d8933844eb6754 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 04:18:25 -0400 Subject: [PATCH 01/15] %s => %d for error count --- src/zm_ffmpeg_camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 9bf257f19..781424b8b 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -904,7 +904,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet); if ( ret < 0 ) { - Warning("Unable to receive frame %d: %s, continuing. error count is %s", + Warning("Unable to receive frame %d: %s, continuing. error count is %d", frameCount, av_make_error_string(ret).c_str(), error_count); error_count += 1; if ( error_count > 100 ) { From 763fe16a0418d54474d2b315594a6cad6cb345d1 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 10:20:23 -0400 Subject: [PATCH 02/15] Google code style, merge some fprintf's --- src/zm_fifo.cpp | 468 +++++++++++++++++++++++++----------------------- 1 file changed, 242 insertions(+), 226 deletions(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 538b696fa..96e7c845e 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -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 -#include -#include +#include #include #include #include - + #include "zm.h" -#include "zm_db.h" #include "zm_time.h" -#include "zm_mpeg.h" #include "zm_signal.h" #include "zm_monitor.h" #include "zm_fifo.h" @@ -17,231 +33,231 @@ static bool zm_fifodbg_inited = false; FILE *zm_fifodbg_log_fd = 0; char zm_fifodbg_log[PATH_MAX] = ""; -static bool zmFifoDbgOpen(){ - if (zm_fifodbg_log_fd) - fclose(zm_fifodbg_log_fd); - zm_fifodbg_log_fd = NULL; - signal(SIGPIPE, SIG_IGN); - FifoStream::fifo_create_if_missing(zm_fifodbg_log); - int fd = open(zm_fifodbg_log,O_WRONLY|O_NONBLOCK|O_TRUNC); - if (fd < 0) - return ( false ); - int res = flock(fd,LOCK_EX | LOCK_NB); - if (res < 0) - { - close(fd); - return ( false ); - } - zm_fifodbg_log_fd = fdopen(fd,"wb"); - if (zm_fifodbg_log_fd == NULL) - { - close(fd); - return ( false ); - } - return ( true ); -} -int zmFifoDbgInit(Monitor *monitor){ - zm_fifodbg_inited = true; - snprintf( zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log", monitor->getStorage()->Path(), monitor->Id() ); - zmFifoDbgOpen(); - return 1; -} -void zmFifoDbgOutput( int hex, const char * const file, const int line, const int level, const char *fstring, ... ){ - char dbg_string[8192]; - va_list arg_ptr; - if (! zm_fifodbg_inited) - return; - if (! zm_fifodbg_log_fd && ! zmFifoDbgOpen()) - return; - - char *dbg_ptr = dbg_string; - va_start( arg_ptr, fstring ); - if ( hex ) - { - unsigned char *data = va_arg( arg_ptr, unsigned char * ); - int len = va_arg( arg_ptr, int ); - int i; - dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), "%d:", len ); - for ( i = 0; i < len; i++ ) - { - dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), " %02x", data[i] ); - } - } - else - { - dbg_ptr += vsnprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), fstring, arg_ptr ); - } - va_end(arg_ptr); - strncpy( dbg_ptr++, "\n", 1); - int res = fwrite( dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd ); - if (res != 1){ - fclose(zm_fifodbg_log_fd); - zm_fifodbg_log_fd = NULL; - } - else - { - fflush(zm_fifodbg_log_fd); - } -} -bool FifoStream::sendRAWFrames(){ - static unsigned char buffer[RAW_BUFFER]; - int fd = open(stream_path,O_RDONLY); - if (fd < 0) - { - Error( "Can't open %s: %s", stream_path, strerror(errno)); - return( false ); - } - while( (bytes_read = read(fd,buffer,RAW_BUFFER)) ) - { - if (bytes_read == 0) - continue; - if (bytes_read < 0) - { - Error( "Problem during reading: %s", strerror(errno)); - close(fd); - return( false ); - } - if ( fwrite( buffer, bytes_read, 1, stdout ) != 1){ - Error( "Problem during writing: %s", strerror(errno)); - close(fd); - return( false ); - } - fflush( stdout ); - } - close(fd); - return ( true ); +static bool zmFifoDbgOpen() { + if ( zm_fifodbg_log_fd ) + fclose(zm_fifodbg_log_fd); + zm_fifodbg_log_fd = NULL; + signal(SIGPIPE, SIG_IGN); + FifoStream::fifo_create_if_missing(zm_fifodbg_log); + int fd = open(zm_fifodbg_log, O_WRONLY|O_NONBLOCK|O_TRUNC); + if ( fd < 0 ) + return false; + int res = flock(fd, LOCK_EX | LOCK_NB); + if ( res < 0 ) { + close(fd); + return false; + } + zm_fifodbg_log_fd = fdopen(fd, "wb"); + if ( zm_fifodbg_log_fd == NULL ) { + close(fd); + return false; + } + return true; } -void FifoStream::file_create_if_missing(const char * path, bool is_fifo,bool delete_fake_fifo){ - static struct stat st; - if(stat(path,&st) == 0){ - - if (! is_fifo || S_ISFIFO(st.st_mode) || ! delete_fake_fifo) - return; - Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path); - unlink(path); - } - int fd; - if (! is_fifo){ - Debug(5, "Creating non fifo file as requested: %s", path); - fd = open(path,O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR); - close(fd); - return; - } - Debug(5, "Making fifo file of: %s", path); - 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); -} -bool FifoStream::sendMJEGFrames(){ - static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; - int fd = open(stream_path,O_RDONLY); - if (fd < 0) - { - Error( "Can't open %s: %s", stream_path, strerror(errno)); - return( false ); - } - total_read = 0; - while( (bytes_read = read(fd,buffer+total_read,ZM_MAX_IMAGE_SIZE-total_read)) ) - { - if (bytes_read < 0) - { - Error( "Problem during reading: %s", strerror(errno)); - close(fd); - return( false ); - } - total_read+=bytes_read; - } - close(fd); - if (total_read == 0) - return( true ); - if (frame_count%frame_mod != 0) - return (true ); - if (fprintf( stdout, "--ZoneMinderFrame\r\n" ) < 0 ) - { - Error( "Problem during writing: %s", strerror(errno)); - return( false ); - } - - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - fprintf( stdout, "Content-Length: %d\r\n\r\n", total_read ); - if ( fwrite( buffer, total_read, 1, stdout ) != 1){ - Error( "Problem during reading: %s", strerror(errno)); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout); - last_frame_sent = TV_2_FLOAT( now ); - frame_count++; - return( true ); +int zmFifoDbgInit(Monitor *monitor) { + zm_fifodbg_inited = true; + snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log", + monitor->getStorage()->Path(), monitor->Id()); + zmFifoDbgOpen(); + return 1; } -void FifoStream::setStreamStart( const char * path ){ - stream_path = strdup(path); -} -void FifoStream::setStreamStart( int monitor_id, const char * format ){ - char diag_path[PATH_MAX]; - const char * filename; - Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); - - if (! strcmp(format,"reference") ) - { - stream_type = MJPEG; - filename = "diagpipe-r.jpg"; - } - else if (! strcmp(format,"delta")) - { - filename = "diagpipe-d.jpg"; - stream_type = MJPEG; - } - else - { - stream_type = RAW; - filename = "dbgpipe.log"; - } +void zmFifoDbgOutput( + int hex, + const char * const file, + const int line, + const int level, + const char *fstring, + ... + ) { + char dbg_string[8192]; + int str_size = sizeof(dbg_string); - snprintf( diag_path, sizeof(diag_path), "%s/%d/%s", monitor->getStorage()->Path(), monitor->Id(), filename ); - setStreamStart(diag_path); -} -void FifoStream::runStream(){ - if (stream_type == MJPEG) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); - else - fprintf( stdout, "Content-Type: text/html\r\n\r\n" ); + va_list arg_ptr; + if ( (!zm_fifodbg_inited) || ( !zm_fifodbg_log_fd && !zmFifoDbgOpen() ) ) + return; - char lock_file[PATH_MAX]; - strcpy(lock_file,stream_path); - strcat(lock_file,".rlock"); - file_create_if_missing(lock_file,false); - int fd_lock = open(lock_file,O_RDONLY); - if (fd_lock < 0) - { - Error( "Can't open %s: %s", lock_file, strerror(errno)); - return; - } - int res = flock(fd_lock,LOCK_EX | LOCK_NB); - if (res < 0) - { - Error( "Flocking problem on %s: - %s", lock_file, strerror(errno) ); - close(fd_lock); - return; - } - - while( !zm_terminate ) - { - gettimeofday( &now, NULL ); - checkCommandQueue(); - if (stream_type == MJPEG) - { - if (! sendMJEGFrames()) - zm_terminate = true; - } - else - { - if (! sendRAWFrames()) - zm_terminate = true; - } - } - close(fd_lock); + char *dbg_ptr = dbg_string; + va_start(arg_ptr, fstring); + if ( hex ) { + unsigned char *data = va_arg(arg_ptr, unsigned char *); + int len = va_arg(arg_ptr, int); + dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), "%d:", len); + for ( int i = 0; i < len; i++ ) { + dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), " %02x", data[i]); + } + } else { + dbg_ptr += vsnprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), fstring, arg_ptr); + } + va_end(arg_ptr); + strncpy(dbg_ptr++, "\n", 1); + int res = fwrite(dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd); + if ( res != 1 ) { + fclose(zm_fifodbg_log_fd); + zm_fifodbg_log_fd = NULL; + } else { + fflush(zm_fifodbg_log_fd); + } +} + +bool FifoStream::sendRAWFrames() { + static unsigned char buffer[RAW_BUFFER]; + int fd = open(stream_path, O_RDONLY); + if ( fd < 0 ) { + Error("Can't open %s: %s", stream_path, strerror(errno)); + return false; + } + while ( (bytes_read = read(fd, buffer, RAW_BUFFER)) ) { + if ( bytes_read == 0 ) + continue; + if ( bytes_read < 0 ) { + Error("Problem during reading: %s", strerror(errno)); + close(fd); + return false; + } + if ( fwrite(buffer, bytes_read, 1, stdout) != 1 ) { + Error("Problem during writing: %s", strerror(errno)); + close(fd); + return false; + } + fflush(stdout); + } + close(fd); + return true; +} + +void FifoStream::file_create_if_missing( + const char * path, + bool is_fifo, + bool delete_fake_fifo + ) { + static struct stat st; + if ( stat(path, &st) == 0 ) { + if ( (!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo ) + return; + Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path); + unlink(path); + } + int fd; + if ( !is_fifo ) { + Debug(5, "Creating non fifo file as requested: %s", path); + fd = open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); + close(fd); + return; + } + Debug(5, "Making fifo file of: %s", path); + 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); +} + +bool FifoStream::sendMJEGFrames() { + static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; + int fd = open(stream_path, O_RDONLY); + if ( fd < 0 ) { + Error("Can't open %s: %s", stream_path, strerror(errno)); + return false; + } + total_read = 0; + while ( + (bytes_read = read(fd, buffer+total_read, ZM_MAX_IMAGE_SIZE-total_read)) + ) { + if ( bytes_read < 0 ) { + Error("Problem during reading: %s", strerror(errno)); + close(fd); + return false; + } + total_read += bytes_read; + } + close(fd); + + if ( (total_read == 0) || (frame_count%frame_mod != 0) ) + return true; + + if ( fprintf(stdout, + "--ZoneMinderFrame\r\n" + "Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n\r\n", + total_read) < 0 ) { + Error("Problem during writing: %s", strerror(errno)); + return false; + } + + if ( fwrite(buffer, total_read, 1, stdout) != 1 ) { + Error("Problem during reading: %s", strerror(errno)); + return false; + } + fprintf(stdout, "\r\n\r\n"); + fflush(stdout); + last_frame_sent = TV_2_FLOAT(now); + frame_count++; + return true; +} + +void FifoStream::setStreamStart(const char * path) { + stream_path = strdup(path); +} + +void FifoStream::setStreamStart(int monitor_id, const char * format) { + char diag_path[PATH_MAX]; + const char * filename; + Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY); + + if ( !strcmp(format, "reference") ) { + stream_type = MJPEG; + filename = "diagpipe-r.jpg"; + } else if ( !strcmp(format, "delta") ) { + filename = "diagpipe-d.jpg"; + stream_type = MJPEG; + } else { + stream_type = RAW; + filename = "dbgpipe.log"; + } + + snprintf(diag_path, sizeof(diag_path), "%s/%d/%s", + monitor->getStorage()->Path(), monitor->Id(), filename); + setStreamStart(diag_path); +} + +void FifoStream::runStream() { + if ( stream_type == MJPEG ) { + fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n"); + } else { + fprintf(stdout, "Content-Type: text/html\r\n\r\n"); + } + + char lock_file[PATH_MAX]; + snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path); + file_create_if_missing(lock_file, false); + + int fd_lock = open(lock_file, O_RDONLY); + if ( fd_lock < 0 ) { + Error("Can't open %s: %s", lock_file, strerror(errno)); + return; + } + int res = flock(fd_lock, LOCK_EX | LOCK_NB); + if ( res < 0 ) { + Error("Flocking problem on %s: - %s", lock_file, strerror(errno)); + close(fd_lock); + return; + } + + while ( !zm_terminate ) { + gettimeofday(&now, NULL); + checkCommandQueue(); + if ( stream_type == MJPEG ) { + if ( !sendMJEGFrames() ) + zm_terminate = true; + } else { + if ( !sendRAWFrames() ) + zm_terminate = true; + } + } + close(fd_lock); } From 6a87ae0fa79ff7bb73e8de8db2fb3dfce049e1fa Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 10:24:51 -0400 Subject: [PATCH 03/15] fix compile warning by copying two bytes, which will grab the \0 after the \n --- src/zm_fifo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_fifo.cpp b/src/zm_fifo.cpp index 96e7c845e..0c0f836bd 100644 --- a/src/zm_fifo.cpp +++ b/src/zm_fifo.cpp @@ -91,7 +91,7 @@ void zmFifoDbgOutput( dbg_ptr += vsnprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), fstring, 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); if ( res != 1 ) { fclose(zm_fifodbg_log_fd); From 6ed29a3d563f508da1619431964219b4cccaaaaf Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 10:29:47 -0400 Subject: [PATCH 04/15] Google code style, ifdef out all the extra includs that shouldn't be in the .h --- src/zm_fifo.h | 93 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/src/zm_fifo.h b/src/zm_fifo.h index 8d1b1ae8c..065fd569c 100644 --- a/src/zm_fifo.h +++ b/src/zm_fifo.h @@ -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 #define ZM_FIFO_H +#if 0 #include #include #include @@ -13,49 +32,55 @@ #include "zm.h" #include "zm_image.h" +#endif #include "zm_monitor.h" #include "zm_stream.h" - -#define zmFifoDbgPrintf(level,params...) {\ - zmFifoDbgOutput( 0, __FILE__, __LINE__, level, ##params );\ - } +#define zmFifoDbgPrintf(level, params...) {\ + zmFifoDbgOutput(0, __FILE__, __LINE__, level, ##params);\ + } #ifndef ZM_DBG_OFF -#define FifoDebug(level,params...) zmFifoDbgPrintf(level,##params) +#define FifoDebug(level, params...) zmFifoDbgPrintf(level, ##params) #else -#define FifoDebug(level,params...) +#define FifoDebug(level, params...) #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); -class FifoStream : public StreamBase -{ +class FifoStream : public StreamBase { + private: + char * stream_path; + int fd; + int total_read; + int bytes_read; + unsigned int frame_count; + static void file_create_if_missing( + const char * path, + bool is_fifo, + bool delete_fake_fifo = true + ); -private: - char * stream_path; - int fd; - int total_read; - int bytes_read; - unsigned int frame_count; - static void file_create_if_missing(const char * path, bool is_fifo, bool delete_fake_fifo=true); + protected: + typedef enum { MJPEG, RAW } StreamType; + StreamType stream_type; + bool sendMJEGFrames(); + bool sendRAWFrames(); + void processCommand(const CmdMsg *msg) {} -protected: - typedef enum { MJPEG, RAW } StreamType; - StreamType stream_type; - bool sendMJEGFrames( ); - bool sendRAWFrames( ); - 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 ); - - void runStream(); + 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(); }; -#endif +#endif // ZM_FIFO_H From dd57fd95ce4060cf9d4cc7273810a740d1fd316e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 11:11:14 -0400 Subject: [PATCH 05/15] Clean up cruft from videostore api. Fix packetqueue clear_unwanted_packets to take a pre_event_count and take it into consideration when finding the right spot in the queue to start recording. --- src/zm_ffmpeg_camera.cpp | 9 ++---- src/zm_ffmpeg_camera.h | 1 - src/zm_packetqueue.cpp | 53 +++++++++++++++++++++++++++++------ src/zm_packetqueue.h | 2 +- src/zm_remote_camera_rtsp.cpp | 1 - src/zm_videostore.cpp | 1 - src/zm_videostore.h | 1 - 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 781424b8b..5e403b468 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -145,7 +145,6 @@ FfmpegCamera::FfmpegCamera( mRawFrame = NULL; mFrame = NULL; frameCount = 0; - startTime = 0; mCanCapture = false; videoStore = NULL; have_video_keyframe = false; @@ -360,7 +359,6 @@ int FfmpegCamera::OpenFfmpeg() { return -1; } - startTime = av_gettime();//FIXME here or after find_Stream_info Debug(4, "Got stream info"); // Find first video stream present @@ -784,7 +782,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event videoStore = new VideoStore((const char *) event_file, "mp4", mFormatContext->streams[mVideoStreamId], NULL, - startTime, this->getMonitor()); } else { @@ -792,7 +789,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event videoStore = new VideoStore((const char *) event_file, "mp4", mFormatContext->streams[mVideoStreamId], mFormatContext->streams[mAudioStreamId], - startTime, this->getMonitor()); } } else { @@ -802,7 +798,6 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event videoStore = new VideoStore((const char *) event_file, "mp4", mFormatContext->streams[mVideoStreamId], NULL, - startTime, this->getMonitor()); } // end if record_audio @@ -811,7 +806,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event videoStore = NULL; } else { - monitor->SetVideoWriterEventId( last_event_id ); + monitor->SetVideoWriterEventId(last_event_id); // 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. @@ -819,7 +814,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event ZMPacket *queued_packet; // 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() ) ) { AVPacket *avp = queued_packet->av_packet(); diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index 3eb52d1f8..0dbb95805 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -84,7 +84,6 @@ class FfmpegCamera : public Camera { struct SwsContext *mConvertContext; #endif - int64_t startTime; int error_count; public: diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 11f24c2a2..11d610b0b 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -157,12 +157,12 @@ unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_ 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)", - 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 - 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)", - 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; } } @@ -210,25 +210,60 @@ int zm_packetqueue::packet_count( int stream_id ) { return packet_counts[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. if ( pktQueue.empty() ) return; - // Step 1 - find keyframe < recording_started. - // Step 2 - pop packets until we get to the packet in step 2 + // Step 1 - find frame <= recording_started. + // 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::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)", mVideoStreamId); 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; + } + } + + if ( it == pktQueue.rend() ) { + Debug(1, "Didn't find a frame before event starttime. keeping all" ); + return; + } + + for ( ; pre_event_count && it != pktQueue.rend(); -- pre_event_count, ++ it ) { + } + 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; AVPacket *av_packet = &(zm_packet->packet); if ( ( av_packet->flags & AV_PKT_FLAG_KEY ) && ( av_packet->stream_index == mVideoStreamId ) - && - timercmp( &(zm_packet->timestamp), recording_started, <= ) ) { Debug(3, "Found keyframe before start with stream index %d at %d.%d", av_packet->stream_index, diff --git a/src/zm_packetqueue.h b/src/zm_packetqueue.h index a02a51ade..0faa281d2 100644 --- a/src/zm_packetqueue.h +++ b/src/zm_packetqueue.h @@ -43,7 +43,7 @@ public: void clearQueue(); void dumpQueue(); 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); private: std::list pktQueue; diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 1f94f4849..a26e2626d 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -418,7 +418,6 @@ int RemoteCameraRtsp::CaptureAndRecord(Image &image, timeval recording, char* ev videoStore = new VideoStore((const char *)event_file, "mp4", mFormatContext->streams[mVideoStreamId], mAudioStreamId==-1?NULL:mFormatContext->streams[mAudioStreamId], - startTime, this->getMonitor() ); strcpy(oldDirectory, event_file); } // end if ! videoStore diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index c39e24098..90ddcc4c3 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -36,7 +36,6 @@ VideoStore::VideoStore( const char *format_in, AVStream *p_video_in_stream, AVStream *p_audio_in_stream, - int64_t nStartTime, Monitor *monitor ) { diff --git a/src/zm_videostore.h b/src/zm_videostore.h index 8e7308e69..fe52cd1dc 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -85,7 +85,6 @@ public: const char *format_in, AVStream *video_in_stream, AVStream *audio_in_stream, - int64_t nStartTime, Monitor * p_monitor); bool open(); ~VideoStore(); From 158b9438d74070f0785d2af9544ea50bdbd1a04d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 27 Jun 2019 21:08:29 +0200 Subject: [PATCH 06/15] Explicitly link with libdl (#2649) Otherwise on some systems linking would fail with undefined reference to symbol 'dlclose@@GLIBC_2.2.5' --- src/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3155fb920..331f9e039 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,10 +20,10 @@ add_executable(zms zms.cpp) include_directories(libbcrypt/include/bcrypt) include_directories(jwt-cpp/include/jwt-cpp) -target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) -target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) -target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} bcrypt) -target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} bcrypt) +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} ${CMAKE_DL_LIBS}) +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} ${CMAKE_DL_LIBS} bcrypt) # Generate man files for the binaries destined for the bin folder FOREACH(CBINARY zma zmc zmu) From ac7d4869de65be78f0e9cdab1349223293a0124d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 15:36:13 -0400 Subject: [PATCH 07/15] We need to seek back pre_event_count video frames, not any frames --- src/zm_packetqueue.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 11d610b0b..f86314113 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -245,14 +245,21 @@ void zm_packetqueue::clear_unwanted_packets( } if ( it == pktQueue.rend() ) { - Debug(1, "Didn't find a frame before event starttime. keeping all" ); + Debug(1, "Didn't find a frame before event starttime. keeping all"); return; } - for ( ; pre_event_count && it != pktQueue.rend(); -- pre_event_count, ++ it ) { + 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" ); + Debug(1, "ran out of pre_event frames before event starttime. keeping all"); return; } From 3bd4486b651ba7d22e9643bf6632636d731b22e5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 15:48:10 -0400 Subject: [PATCH 08/15] Start event when alarm frames >= alarm_frame_count-1 because it is 1based. Add some debug. --- src/zm_monitor.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 76b7e9a34..eba3ca7e9 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1525,21 +1525,25 @@ bool Monitor::Analyse() { Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", name, image_count, event->Id()); closeEvent(); - } - Debug(3, "pre-alarm-count %d", Event::PreAlarmCount()); + } else { // 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; // lets construct alarm cause. It will contain cause + names of zones alarmed std::string alarm_cause = ""; for ( int i=0; i < n_zones; i++ ) { if ( zones[i]->Alarmed() ) { - alarm_cause = alarm_cause + "," + std::string(zones[i]->Label()); + alarm_cause = alarm_cause + "," + std::string(zones[i]->Label()); } } if ( !alarm_cause.empty() ) alarm_cause[0] = ' '; 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", 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 is set, // 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) // from the current position @@ -1559,6 +1565,8 @@ bool Monitor::Analyse() { // Slot is empty, removing image from counter 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); } else { @@ -1569,7 +1577,7 @@ bool Monitor::Analyse() { else 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); // Seek forward the next filled slot in to the buffer (oldest data) From 0ab4f9fce08734cd1b3037e913286dfee78ea991 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 21:49:43 -0400 Subject: [PATCH 09/15] More debugging in packetqueue. --- src/zm_packetqueue.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index f86314113..7668935f0 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -227,7 +227,8 @@ void zm_packetqueue::clear_unwanted_packets( std::list::reverse_iterator it; // Step 1 - find frame <= recording_started. - Debug(3, "Looking for frame before start recording stream id (%d)", mVideoStreamId); + 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 ) { ZMPacket *zm_packet = *it; AVPacket *av_packet = &(zm_packet->packet); @@ -242,6 +243,10 @@ void zm_packetqueue::clear_unwanted_packets( 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() ) { From d972ab60060570a47378c527f4a755a5b599d6da Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 27 Jun 2019 21:50:12 -0400 Subject: [PATCH 10/15] add min_section_length test to alarmed events that go unalarmed --- src/zm_monitor.cpp | 5 ++++- web/skins/classic/css/base/skin.css | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index eba3ca7e9..fda0f5709 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1632,7 +1632,10 @@ bool Monitor::Analyse() { Info("%s: %03d - Gone into alert state", name, image_count); shared_data->state = 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", name, image_count, event->Id(), event->Frames(), event->AlarmFrames()); //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) diff --git a/web/skins/classic/css/base/skin.css b/web/skins/classic/css/base/skin.css index 1dfd6ea90..cd0eaebc3 100644 --- a/web/skins/classic/css/base/skin.css +++ b/web/skins/classic/css/base/skin.css @@ -637,6 +637,7 @@ color:#ffa801; .container-fluid { position: relative; + padding-bottom: 10px; } .sidebar { From 1161c251fc7dfd4691ad8bc10358779d3c009500 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 28 Jun 2019 10:28:53 -0400 Subject: [PATCH 11/15] Add a warning when the monitor is not capturing in live view --- web/skins/classic/views/watch.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/skins/classic/views/watch.php b/web/skins/classic/views/watch.php index 902baaa1c..bef9acd61 100644 --- a/web/skins/classic/views/watch.php +++ b/web/skins/classic/views/watch.php @@ -77,6 +77,11 @@ if ( canView('Control') && $monitor->Type() == 'Local' ) {
+Status() != 'Capturing' ) { + echo '
Monitor is not capturing. We will be unable to provide an image
'; +} +?>
$scale) ); ?>
Type() != 'WebSite' ) { ?> From 6231c64a7a253dac034fb85d27fa5167d0816586 Mon Sep 17 00:00:00 2001 From: CanOfSpam3 Date: Mon, 1 Jul 2019 18:06:10 -0400 Subject: [PATCH 12/15] simplify rtfm step (#2650) save everyone some cruft and time --- docs/installationguide/debian.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst index a6a27aa19..1c85e704c 100644 --- a/docs/installationguide/debian.rst +++ b/docs/installationguide/debian.rst @@ -82,8 +82,7 @@ a read. :: - gunzip /usr/share/doc/zoneminder/README.Debian.gz - cat /usr/share/doc/zoneminder/README.Debian + zcat /usr/share/doc/zoneminder/README.Debian.gz **Step 7:** Enable ZoneMinder service @@ -209,8 +208,7 @@ a read. :: - gunzip /usr/share/doc/zoneminder/README.Debian.gz - cat /usr/share/doc/zoneminder/README.Debian + zcat /usr/share/doc/zoneminder/README.Debian.gz **Step 7:** Setup Database From 2dc60196c5ddb6cb2874c16a291478d0102bdb97 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jul 2019 11:14:24 -0400 Subject: [PATCH 13/15] Code cleanup and cpplint --- src/zm_ffmpeg_camera.cpp | 450 +++++++++++++++++++++------------------ 1 file changed, 237 insertions(+), 213 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 5e403b468..9cf2d93ef 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -1,21 +1,21 @@ // -// 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 -// +// // 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 "zm.h" #include "zm_signal.h" @@ -27,7 +27,7 @@ extern "C" { #include "libavutil/time.h" #if HAVE_LIBAVUTIL_HWCONTEXT_H - #include "libavutil/hwcontext.h" + #include "libavutil/hwcontext.h" #endif } #ifndef AV_ERROR_MAX_STRING_SIZE @@ -39,6 +39,7 @@ extern "C" { #include #include #endif +#include #if HAVE_LIBAVUTIL_HWCONTEXT_H @@ -54,9 +55,11 @@ static enum AVPixelFormat get_hw_format( 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++ ) - 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; } @@ -106,8 +109,7 @@ FfmpegCamera::FfmpegCamera( bool p_capture, bool p_record_audio, const std::string &p_hwaccel_name, - const std::string &p_hwaccel_device - ) : + const std::string &p_hwaccel_device) : Camera( p_id, FFMPEG_SRC, @@ -154,10 +156,11 @@ FfmpegCamera::FfmpegCamera( hw_pix_fmt = AV_PIX_FMT_NONE; #endif -#if HAVE_LIBSWSCALE +#if HAVE_LIBSWSCALE mConvertContext = NULL; #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 ) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = AV_PIX_FMT_RGBA; @@ -170,10 +173,9 @@ FfmpegCamera::FfmpegCamera( } else { Panic("Unexpected colours: %d", colours); } -} // FfmpegCamera::FfmpegCamera +} // FfmpegCamera::FfmpegCamera FfmpegCamera::~FfmpegCamera() { - Close(); if ( capture ) { @@ -203,7 +205,7 @@ int FfmpegCamera::PrimeCapture() { int FfmpegCamera::PreCapture() { // If Reopen was called, then ffmpeg is closed and we need to reopen it. - if ( ! mCanCapture ) + if ( !mCanCapture ) return OpenFfmpeg(); // Nothing to do here return 0; @@ -215,15 +217,20 @@ int FfmpegCamera::Capture(Image &image) { } 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; - while ( !frameComplete && !zm_terminate) { + while ( !frameComplete && !zm_terminate ) { ret = av_read_frame(mFormatContext, &packet); if ( ret < 0 ) { if ( // 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. (ret == -110) ) { @@ -244,7 +251,11 @@ int FfmpegCamera::Capture(Image &image) { Debug(5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts); // 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); if ( ret < 0 ) { Error("Unable to get frame at frame %d: %s, continuing", @@ -264,11 +275,11 @@ int FfmpegCamera::Capture(Image &image) { frameCount++; } else { Debug(4, "Different stream_index %d", packet.stream_index); - } // end if packet.stream_index == mVideoStreamId + } // end if packet.stream_index == mVideoStreamId zm_av_packet_unref(&packet); - } // end while ! frameComplete + } // end while ! frameComplete return frameComplete ? 1 : 0; -} // FfmpegCamera::Capture +} // FfmpegCamera::Capture int FfmpegCamera::PostCapture() { // Nothing to do here @@ -276,7 +287,6 @@ int FfmpegCamera::PostCapture() { } int FfmpegCamera::OpenFfmpeg() { - int ret; have_video_keyframe = false; @@ -290,7 +300,7 @@ int FfmpegCamera::OpenFfmpeg() { AVDictionary *opts = 0; ret = av_dict_parse_string(&opts, Options().c_str(), "=", ",", 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 @@ -306,7 +316,7 @@ int FfmpegCamera::OpenFfmpeg() { } else { 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 ) { Warning("Could not set rtsp_transport method '%s'", method.c_str()); @@ -314,11 +324,11 @@ int FfmpegCamera::OpenFfmpeg() { Debug(1, "Calling avformat_open_input for %s", mPath.c_str()); - mFormatContext = avformat_alloc_context( ); + mFormatContext = avformat_alloc_context(); // Speed up find_stream_info - //FIXME can speed up initial analysis but need sensible parameters... - //mFormatContext->probesize = 32; - //mFormatContext->max_analyze_duration = 32; + // FIXME can speed up initial analysis but need sensible parameters... + // mFormatContext->probesize = 32; + // mFormatContext->max_analyze_duration = 32; mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; mFormatContext->interrupt_callback.opaque = this; @@ -326,7 +336,8 @@ int FfmpegCamera::OpenFfmpeg() { if ( ret != 0 ) #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) av_close_input_file(mFormatContext); #else @@ -339,7 +350,7 @@ int FfmpegCamera::OpenFfmpeg() { return -1; } - AVDictionaryEntry *e=NULL; + AVDictionaryEntry *e = NULL; while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { Warning("Option %s not recognized by ffmpeg", e->key); } @@ -348,94 +359,76 @@ int FfmpegCamera::OpenFfmpeg() { Info("Stream open %s, parsing streams...", mPath.c_str()); #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) - Debug(4, "Calling av_find_stream_info"); - if ( av_find_stream_info(mFormatContext) < 0 ) + ret = av_find_stream_info(mFormatContext); #else - Debug(4, "Calling avformat_find_stream_info"); - if ( avformat_find_stream_info(mFormatContext, 0) < 0 ) + ret = avformat_find_stream_info(mFormatContext, 0); #endif - { - Error("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno)); + if ( ret < 0 ) { + Error("Unable to find stream info from %s due to: %s", mPath.c_str(), + av_make_error_string(ret).c_str()); return -1; } - Debug(4, "Got stream info"); - // Find first video stream present // The one we want Might not be the first mVideoStreamId = -1; mAudioStreamId = -1; for ( unsigned int i=0; i < mFormatContext->nb_streams; i++ ) { -#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - if ( mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) { -#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 + AVStream *stream = mFormatContext->streams[i]; + if ( is_video_stream(stream) ) { if ( mVideoStreamId == -1 ) { mVideoStreamId = i; // if we break, then we won't find the audio stream continue; } else { - Debug(2, "Have another video stream." ); + Debug(2, "Have another video 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 + } else if ( is_audio_stream(stream) ) { if ( mAudioStreamId == -1 ) { mAudioStreamId = i; } else { - Debug(2, "Have another audio stream." ); + Debug(2, "Have another audio stream."); } } - } // end foreach stream + } // end foreach stream if ( mVideoStreamId == -1 ) 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 audio stream at index %d", mAudioStreamId); - packetqueue = new zm_packetqueue( mVideoStreamId > mAudioStreamId ? mVideoStreamId : mAudioStreamId ); + Debug(3, "Found video stream at index %d, audio stream at index %d", + mVideoStreamId, mAudioStreamId); + packetqueue = new zm_packetqueue( + (mVideoStreamId > mAudioStreamId) ? mVideoStreamId : mAudioStreamId); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - //mVideoCodecContext = avcodec_alloc_context3(NULL); - //avcodec_parameters_to_context( mVideoCodecContext, mFormatContext->streams[mVideoStreamId]->codecpar ); + // mVideoCodecContext = avcodec_alloc_context3(NULL); + // avcodec_parameters_to_context(mVideoCodecContext, + // mFormatContext->streams[mVideoStreamId]->codecpar); // this isn't copied. - //mVideoCodecContext->time_base = mFormatContext->streams[mVideoStreamId]->codec->time_base; + // mVideoCodecContext->time_base = + // mFormatContext->streams[mVideoStreamId]->codec->time_base; #else #endif 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 - mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY; + mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY; #endif if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) { 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 { - 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 ) ) { - // Try and get the codec from the codec context - Error("Can't find codec for video stream from %s", mPath.c_str()); - return -1; - } + if ( !mVideoCodec ) { + mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id); + if ( !mVideoCodec ) { + // Try and get the codec from the codec context + Error("Can't find codec for video stream from %s", mPath.c_str()); + return -1; + } + } Debug(1, "Video Found decoder %s", mVideoCodec->name); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); @@ -464,23 +457,27 @@ int FfmpegCamera::OpenFfmpeg() { mVideoCodec->name, av_hwdevice_get_type_name(type)); return -1; } - if ( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX ) - && (config->device_type == type) + if ( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) + && (config->device_type == type) ) { hw_pix_fmt = config->pix_fmt; break; } - } // end foreach hwconfig -#else + } // 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)); +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; - Debug(1, "Creating hwdevice for %s", (hwaccel_device != "" ? hwaccel_device.c_str() : "")); - if ((ret = av_hwdevice_ctx_create(&hw_device_ctx, type, - (hwaccel_device != "" ? hwaccel_device.c_str(): NULL), NULL, 0)) < 0) { + 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; } @@ -488,10 +485,10 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt mVideoCodecContext->hw_device_ctx = av_buffer_ref(hw_device_ctx); hwaccel = true; hwFrame = zm_av_frame_alloc(); -#else +#else Warning("HWAccel support not compiled in."); #endif - } // end if hwacel_name + } // end if hwacel_name // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) @@ -501,7 +498,7 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt #endif e = 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 ) { Error("Unable to open codec for video stream from %s", mPath.c_str()); @@ -528,28 +525,28 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt } else { #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) mAudioCodecContext = avcodec_alloc_context3(mAudioCodec); - avcodec_parameters_to_context( mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar ); + avcodec_parameters_to_context( + mAudioCodecContext, + mFormatContext->streams[mAudioStreamId]->codecpar + ); #else mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec; // = avcodec_alloc_context3(mAudioCodec); #endif - Debug(1, "Audio Found decoder"); zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0); // Open the codec #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) - Debug(1, "Calling avcodec_open"); if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) { #else - Debug(1, "Calling avcodec_open2" ); if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) { #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; } zm_dump_codec(mAudioCodecContext); - } // end if find decoder - } // end if have audio_context + } // end if find decoder + } // end if have audio_context // Allocate space for the native video frame mRawFrame = zm_av_frame_alloc(); @@ -562,23 +559,18 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt return -1; } - Debug( 3, "Allocated frames"); - #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 - int pSize = avpicture_get_size( imagePixFormat, width, height ); + int pSize = avpicture_get_size(imagePixFormat, width, height); #endif 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; } - Debug(4, "Validated imagesize"); - #if HAVE_LIBSWSCALE - Debug(1, "Calling sws_isSupportedInput"); if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) { Error("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, @@ -600,7 +592,7 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt } # 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( mVideoCodecContext->width, mVideoCodecContext->height, @@ -609,13 +601,14 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt imagePixFormat, SWS_BICUBIC, NULL, NULL, 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; } #endif -#else // HAVE_LIBSWSCALE - Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); -#endif // HAVE_LIBSWSCALE +#else // HAVE_LIBSWSCALE + Fatal("You must compile ffmpeg with the --enable-swscale " + "option to use ffmpeg cameras"); +#endif // HAVE_LIBSWSCALE if ( ((unsigned int)mVideoCodecContext->width != width) @@ -623,17 +616,15 @@ Debug(1, "Selected gw_pix_fmt %d %s", hw_pix_fmt, av_get_pix_fmt_name(hw_pix_fmt ((unsigned int)mVideoCodecContext->height != height) ) { 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; return 0; -} // int FfmpegCamera::OpenFfmpeg() +} // int FfmpegCamera::OpenFfmpeg() int FfmpegCamera::Close() { - Debug(2, "CloseFfmpeg called."); mCanCapture = false; @@ -661,18 +652,17 @@ int FfmpegCamera::Close() { if ( mVideoCodecContext ) { avcodec_close(mVideoCodecContext); - Debug(1,"After codec close"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - //avcodec_free_context(&mVideoCodecContext); + // avcodec_free_context(&mVideoCodecContext); #endif - mVideoCodecContext = NULL; // Freed by av_close_input_file + mVideoCodecContext = NULL; // Freed by av_close_input_file } if ( mAudioCodecContext ) { avcodec_close(mAudioCodecContext); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) avcodec_free_context(&mAudioCodecContext); #endif - mAudioCodecContext = NULL; // Freed by av_close_input_file + mAudioCodecContext = NULL; // Freed by av_close_input_file } if ( mFormatContext ) { @@ -690,15 +680,19 @@ int FfmpegCamera::Close() { } return 0; -} // end FfmpegCamera::Close +} // end FfmpegCamera::Close -//Function to handle capture and store -int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) { +// Function to handle capture and store +int FfmpegCamera::CaptureAndRecord( + Image &image, + timeval recording, + char* event_file + ) { if ( !mCanCapture ) { return -1; } int ret; - + int frameComplete = false; while ( !frameComplete ) { av_init_packet(&packet); @@ -707,7 +701,10 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( ret < 0 ) { if ( // 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. (ret == -110) ) { @@ -721,9 +718,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event } if ( (packet.pts != AV_NOPTS_VALUE) && (packet.pts < -100000) ) { - // Ignore packets that have crazy negative pts. They aren't supposed to happen. - 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"); + // Ignore packets that have crazy negative pts. + // They aren't supposed to happen. + 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 ) { Error("Bad packet count over 100, going to close and re-open stream"); return -1; @@ -736,14 +738,16 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event int keyframe = packet.flags & AV_PKT_FLAG_KEY; 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 ) { packet.dts = packet.pts; } // Video recording if ( recording.tv_sec ) { - uint32_t last_event_id = monitor->GetLastEventId(); uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); @@ -754,26 +758,27 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( videoStore ) { 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. if ( packet.stream_index == mVideoStreamId ) { - //Write the packet to our video store + // Write the packet to our video store 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."); } - } // end if video + } // end if video delete videoStore; videoStore = NULL; have_video_keyframe = false; monitor->SetVideoWriterEventId(0); - } // end if videoStore - } // end if end of recording + } // end if videoStore + } // end if end of recording - if ( last_event_id and !videoStore ) { - //Instantiate the video storage module + if ( last_event_id && !videoStore ) { + // Instantiate the video storage module packetqueue->dumpQueue(); if ( record_audio ) { @@ -799,9 +804,9 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event mFormatContext->streams[mVideoStreamId], NULL, this->getMonitor()); - } // end if record_audio + } // end if record_audio - if ( ! videoStore->open() ) { + if ( !videoStore->open() ) { delete videoStore; videoStore = NULL; @@ -809,60 +814,72 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event monitor->SetVideoWriterEventId(last_event_id); // 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; ZMPacket *queued_packet; // Clear all packets that predate the moment when the recording began - packetqueue->clear_unwanted_packets(&recording, monitor->GetPreEventCount(), 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(); 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)", - 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 ) { ret = videoStore->writeVideoFramePacket(avp); have_video_keyframe = true; } else if ( avp->stream_index == mAudioStreamId ) { ret = videoStore->writeAudioFramePacket(avp); } 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; } if ( ret < 0 ) { // Less than zero and we skipped a frame } delete queued_packet; - } // end while packets in the packetqueue + } // end while packets in the packetqueue Debug(2, "Wrote %d queued packets", packet_count); } - } // end if ! was recording + } // end if ! was recording } else { // Not recording - + if ( videoStore ) { - Debug(1,"Deleting videoStore instance"); + Debug(1, "Deleting videoStore instance"); delete videoStore; videoStore = NULL; have_video_keyframe = false; monitor->SetVideoWriterEventId(0); } - } // end if recording or not + } // end if recording or not // Buffer video packets, since we are not recording. // All audio packets are keyframes, so only if it's a video keyframe if ( packet.stream_index == mVideoStreamId ) { if ( keyframe ) { Debug(3, "Clearing queue"); - if ( 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", + if ( + 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(), - packetqueue->packet_count(mVideoStreamId)+1 - ); + packetqueue->packet_count(mVideoStreamId)+1); } packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId); @@ -870,36 +887,29 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event } else if ( packetqueue->size() ) { // it's a keyframe or we already have something in the queue packetqueue->queuePacket(&packet); - } + } } else if ( packet.stream_index == mAudioStreamId ) { - // The following lines should 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 it's audio, and we are doing audio, and there is already something in the queue + // Ensure that the queue always begins with a video keyframe + if ( record_audio && packetqueue->size() ) { packetqueue->queuePacket(&packet); } - } // end if packet type + } // end if packet type if ( packet.stream_index == mVideoStreamId ) { - // only do decode if we have had a keyframe, should save a few cycles. - if ( have_video_keyframe || keyframe ) { - - if ( videoStore ) { - //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket(&packet); - if ( ret < 0 ) { //Less than zero and we skipped a frame - zm_av_packet_unref(&packet); - return 0; - } + if ( (have_video_keyframe || keyframe) && videoStore ) { + int ret = videoStore->writeVideoFramePacket(&packet); + if ( ret < 0 ) { + // Less than zero and we skipped a frame + Error("Unable to write video packet %d: %s", + frameCount, av_make_error_string(ret).c_str()); + } else { have_video_keyframe = true; } - } // end if keyframe or have_video_keyframe - - Debug(4, "about to decode video"); + } // end if keyframe or have_video_keyframe ret = zm_receive_frame(mVideoCodecContext, mRawFrame, packet); if ( ret < 0 ) { - Warning("Unable to receive frame %d: %s, continuing. error count is %d", + Warning("Unable to receive frame %d: %s. error count is %d", frameCount, av_make_error_string(ret).c_str(), error_count); error_count += 1; if ( error_count > 100 ) { @@ -909,10 +919,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event zm_av_packet_unref(&packet); continue; } - if ( error_count > 0 ) error_count --; + if ( error_count > 0 ) error_count--; zm_dump_video_frame(mRawFrame); #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 */ ret = av_hwframe_transfer_data(hwFrame, mRawFrame, 0); if ( ret < 0 ) { @@ -920,7 +934,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event frameCount, av_make_error_string(ret).c_str()); zm_av_packet_unref(&packet); continue; - } + } zm_dump_video_frame(hwFrame, "After hwtransfer"); hwFrame->pts = mRawFrame->pts; @@ -940,23 +954,23 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event frameComplete = 1; 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; if ( videoStore ) { if ( record_audio ) { if ( have_video_keyframe ) { - Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", - mAudioStreamId, packet.stream_index); - //Write the packet to our video store - //FIXME no relevance of last key frame - int ret = videoStore->writeAudioFramePacket(&packet); - if ( ret < 0 ) {//Less than zero and we skipped a frame - Warning("Failure to write audio packet."); - zm_av_packet_unref(&packet); - return 0; - } + // Write the packet to our video store + // FIXME no relevance of last key frame + int ret = videoStore->writeAudioFramePacket(&packet); + if ( ret < 0 ) { + // Less than zero and we skipped a frame + Warning("Failure to write audio packet."); + zm_av_packet_unref(&packet); + return 0; + } } 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 { Debug(4, "Not doing recording of audio packet"); @@ -970,20 +984,27 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event #if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0) Debug(3, "Some other stream index %d, %s", 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 Debug(3, "Some other stream index %d", packet.stream_index); #endif - } // 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. - zm_av_packet_unref(&packet); - } // end while ! frameComplete - return frameCount; -} // end FfmpegCamera::CaptureAndRecord + } // end if is video or audio or something else -int FfmpegCamera::transfer_to_image(Image &image, AVFrame *output_frame, AVFrame *input_frame) { + // 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); + } // end while ! frameComplete + return frameCount; +} // end FfmpegCamera::CaptureAndRecord + +int FfmpegCamera::transfer_to_image( + Image &image, + AVFrame *output_frame, + AVFrame *input_frame + ) { uint8_t* directbuffer; /* Request a writeable buffer of the target image */ @@ -1010,33 +1031,36 @@ int FfmpegCamera::transfer_to_image(Image &image, AVFrame *output_frame, AVFrame NULL, NULL); if ( mConvertContext == NULL ) { Error("Unable to create conversion context for %s from %s to %s", - mPath.c_str(), - av_get_pix_fmt_name((AVPixelFormat)input_frame->format), - av_get_pix_fmt_name(imagePixFormat) - ); - return -1; + mPath.c_str(), + av_get_pix_fmt_name((AVPixelFormat)input_frame->format), + av_get_pix_fmt_name(imagePixFormat) + ); + return -1; } } - if ( sws_scale(mConvertContext, input_frame->data, input_frame->linesize, - 0, mVideoCodecContext->height, output_frame->data, output_frame->linesize) <= 0 ) { - Error("Unable to convert raw format %u to target format %u at frame %d codec %u ", + if ( sws_scale( + mConvertContext, input_frame->data, input_frame->linesize, + 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, imagePixFormat, frameCount, mVideoCodecContext->pix_fmt ); return -1; } -#else // HAVE_LIBSWSCALE - Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); -#endif // HAVE_LIBSWSCALE +#else // HAVE_LIBSWSCALE + Fatal("You must compile ffmpeg with the --enable-swscale " + "option to use ffmpeg cameras"); +#endif // HAVE_LIBSWSCALE 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) { - //FfmpegCamera* camera = reinterpret_cast(ctx); - //Debug(4, "FfmpegInterruptCallback"); + // FfmpegCamera* camera = reinterpret_cast(ctx); + // Debug(4, "FfmpegInterruptCallback"); return zm_terminate; } -#endif // HAVE_LIBAVFORMAT +#endif // HAVE_LIBAVFORMAT From 4e8ac4706016ccf6fb383c252c9874afc6be5aac Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jul 2019 11:58:22 -0400 Subject: [PATCH 14/15] Use the video_first_pts to set the audio_first_pts --- src/zm_videostore.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 90ddcc4c3..da9ec6a9c 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -890,12 +890,20 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { // Scale the PTS of the outgoing packet to be the correct time base 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) ) { // This is the first packet. opkt.pts = 0; Debug(2, "Starting video first_pts will become %" PRId64, ipkt->pts); video_first_pts = ipkt->pts; +#if 1 + // 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 { opkt.pts = av_rescale_q( ipkt->pts - video_first_pts, @@ -923,6 +931,15 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { opkt.dts = 0; Debug(1, "Starting video first_dts will become (%" PRId64 ")", ipkt->dts); video_first_dts = ipkt->dts; +#if 1 + // 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 { opkt.dts = av_rescale_q( ipkt->dts - video_first_dts, @@ -1092,7 +1109,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) { if ( !audio_first_pts ) { opkt.pts = 0; audio_first_pts = ipkt->pts; - Debug(1, "No video_first_pts"); + Debug(1, "No audio_first_pts"); } else { opkt.pts = av_rescale_q( ipkt->pts - audio_first_pts, From 5ea5bd9bdefd5293ce912923e85b592ba72d447c Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Tue, 2 Jul 2019 12:26:33 -0400 Subject: [PATCH 15/15] Only do audio_first_pts if we have audio_in_stream --- src/zm_videostore.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index da9ec6a9c..bd4f47a2f 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -896,13 +896,15 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { Debug(2, "Starting video first_pts will become %" PRId64, ipkt->pts); video_first_pts = ipkt->pts; #if 1 - // 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); + 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 { opkt.pts = av_rescale_q( @@ -932,13 +934,15 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) { Debug(1, "Starting video first_dts will become (%" PRId64 ")", ipkt->dts); video_first_dts = ipkt->dts; #if 1 - // 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); + 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 { opkt.dts = av_rescale_q(