I have an idea why multiple zmc's can seemingly access a single decode pipeline concurrently. I think the zmc's are able to successfully open the codec but they dont have exclusive access to it. That's why it never falls over to software decoding regardless of having more than 1 zmc. It can however fail on decode if the port is not available because it is busy servicing one of the other zmc's. The following code will probably never get to execute the second line.
The solution is to specify the use of the h264_mmal codec on monitor creation and edit.
I created a rough patch to allow for the functionality of selecting the hardware decoder at the monitor creation and edit screen to test the idea. By selecting Ffmpeghw, the zmc for that monitor will specifically use the h264_mmal codec. The user can still select Ffmpeg for software decoding which is default. I made changes to the web UI for this and also some db changes:
The RPI seems to max out at 1 h264 camera at 704x576 resolution plus another at 1280x720 for hw decoding. However, with the patch, additional monitors can now use the software decoder.
The following setup resulted in a load of 3.
I am limited in my testing as I still have two cameras ( just was utilizing the sub stream of one of the cameras for the third monitor). The SV3C camera can be configured for 1920x1080 or 1280x720 for the main stream and 704x576 or 640x360 for the sub stream.
Here's the rough patch.
Code: Select all
diff -Naur ../src-versions/src-orig/zm_ffmpeg_camera.cpp src/zm_ffmpeg_camera.cpp
--- ../src-versions/src-orig/zm_ffmpeg_camera.cpp 2017-05-14 22:01:18.765897796 +0000
+++ src/zm_ffmpeg_camera.cpp 2017-05-14 22:02:26.205693726 +0000
@@ -33,11 +33,12 @@
#include <pthread.h>
#endif
-FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
+FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, int codec_type ) :
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ),
mPath( p_path ),
mMethod( p_method ),
- mOptions( p_options )
+ mOptions( p_options ),
+ h264_codec(codec_type)
{
if ( capture )
{
@@ -312,9 +313,15 @@
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
// Try and get the codec from the codec context
- if ( (mCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL )
- if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
- Fatal( "Can't find codec for video stream from %s", mPath.c_str() );
+ if (h264_codec) {
+ if ( (mCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL )
+ Fatal( "Can't find h/w codec for video stream from %s", mPath.c_str() );
+ }
+
+ if (!h264_codec) {
+ if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
+ Fatal( "Can't find s/w codec for video stream from %s", mPath.c_str() );
+ }
Debug ( 1, "Found decoder" );
diff -Naur ../src-versions/src-orig/zm_ffmpeg_camera.h src/zm_ffmpeg_camera.h
--- ../src-versions/src-orig/zm_ffmpeg_camera.h 2017-05-14 22:01:18.765897796 +0000
+++ src/zm_ffmpeg_camera.h 2017-05-14 22:02:26.205693726 +0000
@@ -57,6 +57,7 @@
bool mCanCapture;
int mOpenStart;
pthread_t mReopenThread;
+ int h264_codec;
#endif // HAVE_LIBAVFORMAT
#if HAVE_LIBSWSCALE
@@ -64,7 +65,7 @@
#endif
public:
- FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
+ FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, int codec_type );
~FfmpegCamera();
const std::string &Path() const { return( mPath ); }
diff -Naur ../src-versions/src-orig/zm_monitor.cpp src/zm_monitor.cpp
--- ../src-versions/src-orig/zm_monitor.cpp 2017-05-14 22:01:18.765897796 +0000
+++ src/zm_monitor.cpp 2017-05-14 22:02:26.285693484 +0000
@@ -2634,7 +2634,152 @@
contrast,
hue,
colour,
- purpose==CAPTURE
+ purpose==CAPTURE,
+ 0
+ );
+
+ monitors[i] = new Monitor(
+ id,
+ name,
+ server_id,
+ function,
+ enabled,
+ linked_monitors,
+ camera,
+ orientation,
+ deinterlacing,
+ event_prefix,
+ label_format,
+ Coord( label_x, label_y ),
+ label_size,
+ image_buffer_count,
+ warmup_count,
+ pre_event_count,
+ post_event_count,
+ stream_replay_buffer,
+ alarm_frame_count,
+ section_length,
+ frame_skip,
+ motion_frame_skip,
+ analysis_fps,
+ analysis_update_delay,
+ capture_delay,
+ alarm_capture_delay,
+ fps_report_interval,
+ ref_blend_perc,
+ alarm_ref_blend_perc,
+ track_motion,
+ embed_exif,
+ RGB_WHITE,
+ purpose,
+ 0,
+ 0
+ );
+ Zone **zones = 0;
+ int n_zones = Zone::Load( monitors[i], zones );
+ monitors[i]->AddZones( n_zones, zones );
+ monitors[i]->AddPrivacyBitmask( zones );
+ Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones );
+ }
+ if ( mysql_errno( &dbconn ) )
+ {
+ Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
+ exit( mysql_errno( &dbconn ) );
+ }
+ // Yadda yadda
+ mysql_free_result( result );
+
+ return( n_monitors );
+}
+
+int Monitor::LoadFfmpeghwMonitors( const char *file, Monitor **&monitors, Purpose purpose )
+{
+ std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeghw'";
+ if ( file[0] ) {
+ sql += " AND Path = '";
+ sql += file;
+ sql += "'";
+ }
+ if ( staticConfig.SERVER_ID ) {
+ sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID );
+ }
+ Debug( 1, "Loading FFMPEG H/W Monitors with %s", sql.c_str() );
+ MYSQL_RES *result = zmDbFetch( sql.c_str() );
+ if ( ! result ) {
+ Error( "Cannot load FfmpegMonitors" );
+ exit( mysql_errno( &dbconn ) );
+ }
+
+ int n_monitors = mysql_num_rows( result );
+ Debug( 1, "Got %d monitors", n_monitors );
+ delete[] monitors;
+ monitors = new Monitor *[n_monitors];
+ for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
+ {
+ int col = 0;
+
+ int id = atoi(dbrow[col]); col++;
+ const char *name = dbrow[col]; col++;
+ unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
+ int function = atoi(dbrow[col]); col++;
+ int enabled = atoi(dbrow[col]); col++;
+ const char *linked_monitors = dbrow[col]; col++;
+
+ const char *path = dbrow[col]; col++;
+ const char *method = dbrow[col]; col++;
+ const char *options = dbrow[col]; col++;
+
+ int width = atoi(dbrow[col]); col++;
+ int height = atoi(dbrow[col]); col++;
+ int colours = atoi(dbrow[col]); col++;
+ /* int palette = atoi(dbrow[col]); */ col++;
+ Orientation orientation = (Orientation)atoi(dbrow[col]); col++;
+ unsigned int deinterlacing = atoi(dbrow[col]); col++;
+ int brightness = atoi(dbrow[col]); col++;
+ int contrast = atoi(dbrow[col]); col++;
+ int hue = atoi(dbrow[col]); col++;
+ int colour = atoi(dbrow[col]); col++;
+
+ const char *event_prefix = dbrow[col]; col++;
+ const char *label_format = dbrow[col]; col++;
+
+ int label_x = atoi(dbrow[col]); col++;
+ int label_y = atoi(dbrow[col]); col++;
+ int label_size = atoi(dbrow[col]); col++;
+
+ int image_buffer_count = atoi(dbrow[col]); col++;
+ int warmup_count = atoi(dbrow[col]); col++;
+ int pre_event_count = atoi(dbrow[col]); col++;
+ int post_event_count = atoi(dbrow[col]); col++;
+ int stream_replay_buffer = atoi(dbrow[col]); col++;
+ int alarm_frame_count = atoi(dbrow[col]); col++;
+ int section_length = atoi(dbrow[col]); col++;
+ int frame_skip = atoi(dbrow[col]); col++;
+ int motion_frame_skip = atoi(dbrow[col]); col++;
+ double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++;
+ unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0);
+ int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
+ int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++;
+ int fps_report_interval = atoi(dbrow[col]); col++;
+ int ref_blend_perc = atoi(dbrow[col]); col++;
+ int alarm_ref_blend_perc = atoi(dbrow[col]); col++;
+ int track_motion = atoi(dbrow[col]); col++;
+ bool embed_exif = (*dbrow[col] != '0'); col++;
+
+ Camera *camera = new FfmpegCamera(
+ id,
+ path, // File
+ method,
+ options,
+ width,
+ height,
+ colours,
+ brightness,
+ contrast,
+ hue,
+ colour,
+ purpose==CAPTURE,
+ 1
);
monitors[i] = new Monitor(
@@ -2692,6 +2837,8 @@
}
#endif // HAVE_LIBAVFORMAT
+
+
Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose )
{
std::string sql = stringtf( "select Id, Name, ServerId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id );
@@ -2896,12 +3043,36 @@
contrast,
hue,
colour,
- purpose==CAPTURE
+ purpose==CAPTURE,
+ 0
+ );
+#else // HAVE_LIBAVFORMAT
+ Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id );
+#endif // HAVE_LIBAVFORMAT
+ }
+ else if ( type == "Ffmpeghw" )
+ {
+#if HAVE_LIBAVFORMAT
+ camera = new FfmpegCamera(
+ id,
+ path.c_str(),
+ method,
+ options,
+ width,
+ height,
+ colours,
+ brightness,
+ contrast,
+ hue,
+ colour,
+ purpose==CAPTURE,
+ 1
);
#else // HAVE_LIBAVFORMAT
Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id );
#endif // HAVE_LIBAVFORMAT
}
+
else if (type == "Libvlc")
{
#if HAVE_LIBVLC
diff -Naur ../src-versions/src-orig/zm_monitor.h src/zm_monitor.h
--- ../src-versions/src-orig/zm_monitor.h 2017-05-14 22:01:18.765897796 +0000
+++ src/zm_monitor.h 2017-05-14 22:02:26.305693423 +0000
@@ -429,6 +429,7 @@
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose );
#if HAVE_LIBAVFORMAT
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose );
+ static int LoadFfmpeghwMonitors( const char *file, Monitor **&monitors, Purpose purpose );
#endif // HAVE_LIBAVFORMAT
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose );
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );