Skip to content

Camera Quirks Tolerance#4

Merged
steelbrain merged 4 commits into
mainfrom
steelbrain/camera-quirk-tolerance
Apr 27, 2026
Merged

Camera Quirks Tolerance#4
steelbrain merged 4 commits into
mainfrom
steelbrain/camera-quirk-tolerance

Conversation

@steelbrain

Copy link
Copy Markdown
Owner
  • Adds diagnostic callback for consumers
  • Changes previously-fatal errors to diagnostic logs now and keeps the stream going

Introduces RTSPDiagnostic (severity + message) and an
optional onDiagnostic closure on RTSPClientSession.init.
The first emitter: when a camera issues a new Session ID
at audio SETUP that differs from the one returned at
video SETUP, emit a .warning before rolling forward.

Severity .error is reserved for cases where real data is
dropped while the stream lives — distinct from a thrown
RTSPError, which means the stream is dead.
H.264 and H.265 depacketizers previously returned a hard
failure on a too-short payload (zero bytes for H.264,
fewer than 2 bytes for H.265). That failure propagated
up the AsyncThrowingStream and tore the session down.
Some IP camera firmwares emit such packets — likely as
keep-alives or simply buggy framing — and a single one
ended an otherwise-healthy stream.

Both codecs now treat too-short payloads as no-ops and
return success. The session-level video pump emits an
onDiagnostic .warning when an empty payload is observed
so consumers can flag the misbehaving hardware.

Severity is .warning rather than .error: an empty packet
carries no real video data, so nothing was actually lost.
.error stays reserved for cases where data we expected
to deliver gets dropped.

Matches GStreamer rtph264depay (silent debug-log skip)
and Live555 (BufferedPacket discard); FFmpeg/retina/
gortsplib/pion/ExoPlayer all return errors here, but
their callers vary on whether to abort. retina #115
already relaxed the FU-A inner check for the same class
of bug; this extends the same leniency to the outer
zero-byte case.
InorderParser previously threw on an out-of-order RTP
sequence when the transport was TCP-interleaved; the
UDP path silently dropped. The TCP-strict policy was
inherited from upstream retina with no rationale beyond
"TCP guarantees order."

That justification confuses TCP byte-stream ordering
(which is preserved) with RTP-sequence ordering (which
is not). A buggy camera whose internal packetizer writes
packets out-of-order before muxing onto the TCP pipe
will deliver out-of-order RTP regardless of TCP. RFC
3550 §A.1 treats reordering as a normal case receivers
MUST tolerate, and RFC 2326/7826 say nothing about
RTP-sequence guarantees on interleaved transport.

Both transports now drop out-of-order packets. On TCP
this also emits an onDiagnostic .warning so consumers
can surface the camera misbehavior; UDP stays silent
because reordering on UDP is normal network behavior.

Aligns with FFmpeg, GStreamer, Live555, gortsplib, and
ExoPlayer — none of which abort on this case. retina
itself has open issue #40 / PR #111 acknowledging the
same class of bug.

Test added: out-of-order TCP packet is dropped (no
throw) and the diagnostic callback fires once with
.warning severity.
Covers the 0.2.0 fix that swapped DepacketizeError("Empty
NAL" / "Short NAL") for .success(()): empty H.264 RTP
payloads, plus zero- and one-byte H.265 payloads, must
no longer tear down the session. Both depacketizers also
recover cleanly on a subsequent valid packet.
@steelbrain steelbrain merged commit 00d3651 into main Apr 27, 2026
2 checks passed
@steelbrain steelbrain deleted the steelbrain/camera-quirk-tolerance branch April 27, 2026 20:21
steelbrain added a commit that referenced this pull request May 30, 2026
A VPS/SPS/PPS NAL over 65535 bytes trapped building the HEVC record;
reject it up front, mirroring the H264 guard. SimpleAudioDepacketizer
frameLength now returns nil on an oversized payload instead of a
precondition trap. Compute the H.265 conformance-window crop in 64-bit
so malformed offsets can't wrap a 32-bit add to a wrong/zero size, and
require the crop to leave a positive picture. Tighten bit_depth_*_minus8
to the spec range [0,6] (7/8 also collided with the reserved
HEVC-record bit).

Addresses audit findings #4/#19/#31/#32. Adds regression tests for the
NAL-size and oversized-payload guards.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant