The previous implementation used `str_replace($range, '-', $range)` which
is a no-op (wrong arg order, return value discarded), then cast the raw
Range value (e.g. "12345-67890" or "-500") to int. The function then
ignored the requested END entirely and always streamed from the parsed
start to EOF.
For a suffix range like `Range: bytes=-500` -- which Chrome's media stack
sends to locate the moov atom in many HEVC mp4s -- (int)"-500" is -500,
producing Content-Length = filesize + 500. fseek with SEEK_SET fails for
negative offsets, so the body delivered was filesize bytes against an
inflated Content-Length, triggering ERR_CONTENT_LENGTH_MISMATCH in the
browser and blocking HEVC playback in the files view.
Parse `bytes=start-end`, `bytes=start-`, and `bytes=-suffix` per RFC 7233,
clamp the end to file size, return 416 for unsatisfiable ranges, set
Content-Length to the actual byte count served, and stop reading once
that many bytes have been emitted. Guard ob_flush() with ob_get_level()
so it does not warn when no buffer is active.
Verified on pseudo by loading an HEVC mp4 in Chrome -- the
ERR_CONTENT_LENGTH_MISMATCH is gone, the browser parses metadata
(duration, dimensions) and buffers playback data normally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a Profile build type with -O2 -g -fno-omit-frame-pointer so perf
can walk stacks accurately while running optimized code. Use with
cmake -DCMAKE_BUILD_TYPE=Profile ..
When setStreamStart()->loadMonitor() failed (e.g. monitor id not found,
shm not yet mapped), zms continued into runStream() for non-SINGLE
stream types and dereferenced a null Monitor shared_ptr at
monitor->GetFPS(), crashing in get_capture_fps with fault address 0x618.
Bail early after the STREAM_SINGLE branch with a "Not connected" text
frame, mirroring the SINGLE-type recovery. For STREAM_JPEG, emit the
multipart Content-Type first so sendTextFrame produces a well-formed
response. Placed before openComms()/command_processor spawn so there
is no resource teardown to do on the bail path.
The file uses ES module import/export syntax but the global ESLint
config sets sourceType: "script", which caused CI to fail with a parse
error. Add a per-file override that parses it as a module, run
eslint --fix for the now-visible whitespace/indent/semi issues, and
drop two unused locals in createMotionAnalyzer that --fix could not
resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related changes to PacketQueue::clearPackets, called by the analysis
thread on every video packet:
1. Lock-free call-site gate (should_try_clear) on the analysis path.
In keep_keyframes mode the existing early-return at the top of
clearPackets discards most non-keyframe video packets after acquiring
the queue mutex. Add an inline lock-free check at the call site so
non-keyframe packets skip the mutex acquire entirely. clear_packets_pending_
is now std::atomic<bool> so it can be read without the lock; a stale
read is harmless (at worst we make one extra cheap early-returning call).
The !keep_keyframes path always returns true from the gate because that
mode pops one packet at a time on every video packet.
2. Iterator boundary in the scan loop changed from >= to >. Setting
next_front to a packet that an iterator points at is safe because
clearPackets deletes strictly before next_front, so the iterator's
own packet stays in the queue. Previously, an event-start (or other)
iterator landing exactly on a keyframe blocked the leading GOP from
being dropped until the iterator advanced; now we can include that
keyframe as next_front while the iterator continues to point at it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
We check for the file's existence, and if it does, we set AUDIO_MOTION_ENABLED = true.
Okay, let's do an additional check in case the import fails for some reason.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
I tested adding large amounts of data to the DOM, and the synchronous operation of "replaceWith" and "append" in the insertModalHtml() function works quite correctly.
This was just my extra precaution.
When PullMessages failed, Run() looped back into Subscribe() and called
CreatePullPointSubscription on the same soap context without unsubscribing
the previous one. Cameras with per-user pull-point caps (Hikvision,
Reolink, etc.) only release a slot when the originating socket closes, so
each cycle leaked a slot until the camera started rejecting new
subscriptions with ter:NotAuthorized. Only a zmc restart (which closed
sockets and ran the destructor) recovered.
- Subscribe() now calls cleanup_subscription() and tears down the soap
context up front when has_valid_subscription_ is set, forcing a fresh
TCP connection.
- WaitForMessage()'s failure path calls cleanup_subscription() before
going unhealthy so the camera-side slot is released as soon as we know
we're abandoning it.
- try_usernametoken_auth is reset at the start of each subscription cycle
so a transient plain-auth fallback doesn't pin for the lifetime of the
process.
- Auth-error detection in CreatePullPointSubscription now checks
soap_fault_string in addition to soap_fault_detail; the observed fault
carries the "...not authorized" wording in fault_string with detail=null,
so the existing plain-UsernameToken fallback never fired.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>