- Image::AssignDirect(const AVFrame*): both the invalidate() failure
path and the success path overwrote buffer/buffertype without freeing
any previously-owned buffer. If the Image currently owned a
ZM_BUFTYPE_ZM/MALLOC/NEW allocation, that memory was leaked on every
AssignDirect(frame) call. Added DumpImgBuffer() at the top of
invalidate() and immediately before the success-path buffer
reassignment; DumpBuffer is a no-op for DONTFREE buffers, so this is
safe whether the Image previously owned its memory or wrapped a
caller's buffer.
- Switched five remaining av_get_pix_fmt_name() calls flagged by review
to zm_get_pix_fmt_name():
* Monitor::GetAlarmImage warnings (zm_monitor.cpp:1432, 1438-1439)
* Monitor::ReadShmFrame warnings (zm_monitor.cpp:3042, 3049-3050)
* Camera ctor fallback Error (zm_camera.cpp:90)
All can be reached with AV_PIX_FMT_NONE or unrecognised formats where
av_get_pix_fmt_name returns nullptr, which is undefined when fed to %s.
refs #4788
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Camera::linesize/imagesize was changed by the migration to FFALIGN(.,32)
and av_image_get_buffer_size(...,32) to keep SIMD-friendly strides. But
Camera describes the *device-side* buffer that capture paths copy from
(V4L2 mmap buffers, raw RTP frames, etc.). Those external buffers are
tightly packed at the driver/source stride, not 32-byte aligned, so the
inflated imagesize causes:
- LocalCamera::PrimeCapture's `pSize != imagesize` check
(av_image_get_buffer_size(..., align=1) vs Camera::imagesize) to
Fatal for any width that isn't a multiple of 32.
- Other raw-socket capture paths to pass an oversized imagesize as the
source buffer size, risking out-of-bounds reads of driver-allocated
buffers.
Revert Camera::linesize/imagesize to align=1 (tightly packed). Image
internal buffers and SHM slots independently apply 32-byte alignment
where they need it (Monitor::connect already takes
max(camera->ImageSize(), av_image_get_buffer_size(RGBA, w, h, 32)) for
the SHM slot, so the SIMD-aligned slot capacity is preserved).
refs #4788
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Image::Assign: when source linesize > destination linesize, copy only
the destination's row capacity per line; previously copied
image.linesize bytes into rows of smaller linesize, overflowing the
destination buffer on the last row.
- Monitor::CheckSignal: dispatch the 1-byte-per-pixel branch via
zm_bytes_per_pixel(pix_fmt) == 1 so YUV422P/J422P are sampled on the
Y plane like GRAY8/YUV420P, instead of falling through and reporting
"no signal".
- Monitor::WriteShmFrame: record image_pixelformats[index] via
capture_image->PixFormat() (canonical imagePixFormat) instead of
AVPixFormat(), which re-derives from the deprecated
(colours, subpixelorder) pair and could propagate stale metadata.
- Monitor::connect: rewrite stale comment that claimed readers don't
need per-slot pixformat adoption; readers MUST call ReadShmFrame()
to adopt the actual format zmc wrote.
- Camera::Camera: guard linesize/imagesize derivation against
AV_PIX_FMT_NONE and negative returns from av_image_get_linesize /
av_image_get_buffer_size, falling back to width * colours stride so
unsigned wrap-around can't break SHM sizing.
refs #4788
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
linesize and imagesize were still hardcoded to AV_PIX_FMT_YUV420P
sizing despite colours/pixelFormat being set correctly from DB.
For RGBA monitors, shared memory slots were 3.7x too small, causing
buffer overflow and intermittent corruption in the live stream.
Use the Camera's pixelFormat member (set from p_colours/p_subpixelorder)
for linesize and imagesize computation.
refs #4735
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ZM_COLOUR_GRAY8, ZM_COLOUR_YUV420P, and ZM_COLOUR_YUVJ420P were all
defined to 1, making format identification via colours ambiguous.
LocalCamera misidentified YUV420P as GRAY8, causing V4L2 MJPEG cameras
to decode to grayscale via expensive sws_scale conversion.
Replace the legacy ZM_COLOUR_*/ZM_SUBPIX_ORDER_* integer pair with
AVPixelFormat as the single source of truth for pixel format dispatch:
- Add src/zm_pixformat.h with central format helpers:
zm_pixformat_from_colours, zm_colours_from_pixformat,
zm_bytes_per_pixel, zm_db_colours_to_pixformat, zm_is_rgb32,
zm_is_rgb24, zm_is_yuv420
- Add AVPixelFormat pixelFormat member + PixelFormat() accessor to Camera
- Add PixFormat() accessor to Image, delegate AVPixFormat methods
to shared helpers
- Migrate all ~100 format dispatch comparisons in zm_image.cpp,
zm_local_camera.cpp, zm_ffmpeg_camera.cpp, zm_remote_camera_rtsp.cpp,
zm_libvlc_camera.cpp, zm_libvnc_camera.cpp, zm_monitor.cpp,
zm_mpeg.cpp from colours/subpixelorder checks to imagePixFormat/
AVPixelFormat checks
- Deprecate GetFFMPEGPixelFormat, delegate to zm_pixformat_from_colours
- Fix DeColourise bug: imagePixFormat was not updated to GRAY8
- Deprecate ZM_COLOUR_* and ZM_SUBPIX_ORDER_* constants in zm_rgb.h
- Add deprecation notice on Monitor.Colours web UI dropdown
- Add 13 Catch2 test cases (105 assertions) for format mapping helpers
refs #4735
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The existing PTS backward jump check in FfmpegCamera::Capture() does not
catch cases where DTS jumps back significantly (e.g. stream restarts,
encoder resets) while PTS may remain unaffected. This causes the
VideoStore to spend minutes forcing DTS monotonicity on every packet via
the write_packet fixup path, flooding logs with warnings.
Add per-stream DTS tracking (mLastVideoDTS/mLastAudioDTS) and a backward
jump check mirroring the existing PTS check: if DTS jumps back more than
10 seconds, increment error_count and after 6 occurrences return -1 to
trigger capture reconnection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
If avformat_new_stream() returns nullptr, the code would log an error
but then still dereference mVideoStream to get the index, causing a
null pointer dereference crash.
Move the assignment inside the if block where mVideoStream is known
to be valid.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
With this commit a unified structure for includes is introduced.
The general rules:
* Only include what you need
* Include wherever possible in the cpp and forward-declare in the header
The includes are sorted in a local to global fashion. This means for the include order:
0. If cpp file: The corresponding h file and an empty line
1. Includes from the project sorted alphabetically
2. System/library includes sorted alphabetically
3. Conditional includes
* introduce non-loop-unrolled version of function and use them when the image size is not a multiple of 12 or 16
* Remove tests for imagesize being a multiple of 16 or 12 to handle functions with unrolled loops
* Use non-unrolled functions when image size is not a multiple of 12 or 16
* use std_blend when image is odd size
Memory allocations and image size requirements changed to be as needed for 64 byte alignment.
Self-test code for Blend modified accordingly and added Self-test for the delta functions.