ZoneMinder / zoneminder

ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras.
http://www.zoneminder.com/
GNU General Public License v2.0
5.18k stars 1.23k forks source link

zm_eventstream.cpp issues #3437

Closed intc closed 2 years ago

intc commented 2 years ago

Environment

We have two ZM instances. Most of the system packages should be at same versions (except kernel as stated above).

Bug

"Faulty" instance has exactly same symptoms as described here: https://forums.zoneminder.com/viewtopic.php?t=31455 - We observe playback issues when Scale >= Actual (corrupted video). On "OK" instance the playback works without issues.

"Scale == 3/4x" works without issues and no zms_eXXXX.log file is created during the playback.

I have done my best to unify all configuration parameters between the two instances. Storage settings:

Faulty instance creates a zms_eXXXX.log file during the playback - Please see "Debug Logs" below.

I have no idea why the faulty instance attempts to open the .mp4 file even the camera storage settings has Video Writer disabled. Could this be related?

To Reproduce

Open saved event and start playback with Scale >= "Actual". Like said above we have only one zm node which does this.

Expected behavior

Normal clean playback despite selected scaling.

Debug Logs

02/16/22 05:17:40.079895 zms_e49591[18089].ERR-zm_ffmpeg_input.cpp/51 [Could not open input file '/var/lib/zoneminder/events/3/2022-02-15/49591/49591-video.mp4' (error 'No such file or directory')]
02/16/22 05:17:40.080420 zms_e49591[18089].WAR-zm_eventstream.cpp/304 [Unable to open ffmpeg_input /var/lib/zoneminder/events/3/2022-02-15/49591/49591-video.mp4]
02/16/22 05:19:30.694102 zms_e49591[18089].INF-zm_eventstream.cpp/1150 [Unable to send raw frame 151: Broken pipe]
02/16/22 05:19:29.130926 zms_e49591[18118].ERR-zm_ffmpeg_input.cpp/51 [Could not open input file '/var/lib/zoneminder/events/3/2022-02-15/49591/49591-video.mp4' (error 'No such file or directory')]
02/16/22 05:19:29.131488 zms_e49591[18118].WAR-zm_eventstream.cpp/304 [Unable to open ffmpeg_input /var/lib/zoneminder/events/3/2022-02-15/49591/49591-video.mp4]
02/16/22 05:19:29.132015 zms_e49591[18118].WAR-zm_eventstream.cpp/1131 [Unable to send raw frame 1: Resource temporarily unavailable rc 61440 != 208047]
02/16/22 05:19:29.1362 ... 
baudneo commented 2 years ago

It seems to me that even though you have set the source as JPEG on the "faulty" instance, it is trying to grab frames from an expected mp4 container. Can you query your monitor using sudo zmu -m[Monitor ID] -q -U username -P password ?

intc commented 2 years ago
Id : 3
Name : katu
Type : Remote
Protocol : http
Host : 10.xx.xx.xx
Port : 80
Path : /stream?uri=video.pro2
Width : 1920
Height : 1080
Colours : 3
Subpixel Order : 6
Event Prefix : Katu-
Label Format : %N - %d/%m/%y %H:%M:%S
Label Coord : 0,0
Label Size : 1
Image Buffer Count : 3
Warmup Count : 0
Pre Event Count : 5
Post Event Count : 5
Stream Replay Buffer : 0
Alarm Frame Count : 1
Section Length : 600
Min Section Length : 10
Maximum FPS : 0.00
Alarm Maximum FPS : 0.00
Reference Blend %ge : 12
Alarm Reference Blend %ge : 6
Track Motion : 0
Function: 3 - Motion Detection
Zones : 0
Recording Enabled? enabled
Events Enabled (!TRIGGER_OFF)? enabled
Motion Detection Enabled? enabled
baudneo commented 2 years ago

Alright that doesnt have the writer info, can you open a MySQL session and try select Id, VideoWriter, SaveJPEGs from Monitors; or select VideoWriter, SaveJPEGs from Monitors where Id=3;

baudneo commented 2 years ago

Oh, also FYI 'Remote' type has knows issues, can you try ffmpeg monitor type?

intc commented 2 years ago
+----+-------------+-----------+
| Id | VideoWriter | SaveJPEGs |
+----+-------------+-----------+
|  1 |           0 |         3 |
|  2 |           0 |         3 |
|  3 |           0 |         3 |
+----+-------------+-----------+
intc commented 2 years ago

Source type: Ffmpeg - No effect on the playback issue.

Checked against a recorded event generated AFTER source type switch.

Id : 3
Name : rk7 katu
Type : File
Path : http://10.xx.xx.xx/stream?uri=video.pro2
Width : 1920
Height : 1080
...
baudneo commented 2 years ago

Well the DB and settings are in sync, I am unsure as to what the root cause may be. Have you tried running ffmpeg? In another issue, the dev mentions 'Remote' as being problematic.

After reading that forum post it seems like it may also be something internal to ZM in the code. I am assuming you have checked the JPEgs on disk and all is well there? Is this H264 codec?

Can you try running debug logs on this monitor and posting a sanitized gist of the debug logs for zms_m3 so they are available for the dev to look at?

intc commented 2 years ago

JPEGs on the disk are fine (the playback is 100% ok when down scaled - and there's no other source than these files). I try to take some time later with debug options set on the m3 streaming server.

intc commented 2 years ago

Inspired by the log line in zms_eXXXX.log:

02/16/22 05:19:29.132015 zms_e49591[18118].WAR-zm_eventstream.cpp/1131

Altered build/config.h on "faulty" node:

// #define HAVE_SENDFILE 1

Rebuilt ZM -> Playback OK ("Scale == Actual")

intc commented 2 years ago

I reverted my config.h hack and did more proper fix so that sendfile works now. I'm not very happy about the type casts etc. If sendfile fails there will be a warning and EventStream::send_file() will exit with an error.

Quoting sendfile(2) man page:

Note that a successful call to sendfile() may write fewer bytes than requested; the caller should be prepared to retry the call if there were unsent bytes.

The diff:

diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp
index 34f693f36..4d3c115f4 100644
--- a/src/zm_eventstream.cpp
+++ b/src/zm_eventstream.cpp
@@ -1107,6 +1107,7 @@ bool EventStream::send_file(const std::string &filepath) {
   }
 #if HAVE_SENDFILE
   static struct stat filestat;
+  off_t off = 0;
   if (fstat(fileno(fdj), &filestat) < 0) {
     fclose(fdj); /* Close the file handle */
     Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
@@ -1122,14 +1123,21 @@ bool EventStream::send_file(const std::string &filepath) {
     Info("Unable to send raw frame %d: %s", curr_frame_id, strerror(errno));
     return false;
   }
-  int rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size);
-  if (rc == (int)filestat.st_size) {
-    // Success
-    fclose(fdj); /* Close the file handle */
-    return true;
+  // send
+  int count = (int)filestat.st_size;
+  int rc = 0;
+  while (count > 0) {
+       count = count - rc;
+       rc = zm_sendfile(fileno(stdout), fileno(fdj), &off, (size_t)count);
+       if (rc < 0) {
+         Warning("Unable to send raw frame %d: %s, rc: %d, count: %d, off: %d, size: %d",
+           curr_frame_id, strerror(errno), rc, count, (int)off, (int)filestat.st_size);
+         fclose(fdj); /* Close the file handle */
+         return false;
+       }
   }
-  Warning("Unable to send raw frame %d: %s rc %d != %d",
-      curr_frame_id, strerror(errno), rc, (int)filestat.st_size);
+  fclose(fdj); /* Close the file handle */
+  return true;
 #endif

   static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
connortechnology commented 2 years ago

Yeah I was suspecting that. I am reworking it all. Should be able to free up ram as well.