Skip to content

fix(api): flag the _mainv repair from the SERVED SDP, not the producer - #592

Merged
badbread merged 2 commits into
mainfrom
fix/mainv-consumer-sdp-detection
Aug 8, 2026
Merged

fix(api): flag the _mainv repair from the SERVED SDP, not the producer#592
badbread merged 2 commits into
mainfrom
fix/mainv-consumer-sdp-detection

Conversation

@badbread

@badbread badbread commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Follow-up to #591. The opt-in _mainv H.265→H.264 main repair was inert for the exact camera it targeted (the Uniview H.265 LPR main).

Root cause

#591 detects a broken main by running sdp_video_lacks_fmtp on the producer SDP. But the LPR camera's producer SDP does carry an a=fmtp line — it just has sprop-sps + sprop-pps and no sprop-vps. HEVC needs VPS+SPS+PPS, so go2rtc can't assemble a complete parameter set and serves consumers an SDP with no a=fmtp at all — which is what makes Media3 throw missing attribute fmtp.

So the producer verdict is Some(false) ("has fmtp") and the camera is never flagged, while the served SDP is the one that actually breaks clients.

Verified against a live go2rtc:

camera codec served a=fmtp?
lpr H265 ❌ none → Media3 fails
frontyard / driveway / garage H265 ✅ complete, with sprop-vps → plays
sideyard H264 ✅ → plays

Fix

Detect on the SDP go2rtc serves to RTSP consumers:

  • add stream_served_video_lacks_fmtp + StreamIndex::video_lacks_fmtp_served
  • _mainv uses the served verdict; _subv keeps the producer verdict (a sub is often un-consumed, so it has no served SDP — the main is always consumed by the recorder, so its served SDP is reliably present)
  • resolve_needs_subv's sticky-across-unknown semantics unchanged
  • playback.rs's rtsp_mainv_url gate is unchanged (still state.mainv_needed); only the comment is corrected

Unit tests use the real captured SDPs (IPs genericised to RFC 5737). docs/DECISIONS.md gets a dated correction to the #591 entry.

The transcode approach itself (H.265→H.264 re-encode, opt-in, default off) is unchanged — only the trigger is corrected.

#591's _mainv detection reused sdp_video_lacks_fmtp on the MAIN producer SDP.
The reference LPR camera (Uniview H.265) publishes a producer fmtp with
sprop-sps+sprop-pps but no sprop-vps, so the producer verdict is Some(false)
("has fmtp") and the camera is never flagged -- yet go2rtc, unable to build a
complete HEVC parameter set, serves consumers an SDP with no a=fmtp at all,
which is what makes Media3 throw 'missing attribute fmtp'. The merged repair was
therefore inert for exactly the camera it targeted.

Detect on the SDP go2rtc SERVES to RTSP consumers instead: add
stream_served_video_lacks_fmtp + StreamIndex::video_lacks_fmtp_served, used for
_mainv only. _subv keeps the producer side (a sub is often un-consumed, so it has
no served SDP); the main is always consumed by the recorder, so its served SDP
is reliably present. Verified against the live prod go2rtc: LPR served SDP lacks
fmtp while frontyard/driveway/garage (H.265, playable) serve a complete fmtp
with sprop-vps. Unit tests use the real captured SDPs. DECISIONS.md corrected.

Signed-off-by: badbread <badbread@users.noreply.github.com>
badbread added a commit that referenced this pull request Aug 8, 2026
…no-verdict debug log

Addresses the Fable review of #592:
- config.rs / dto.rs doc comments still described PRODUCER-side detection
  (sdp_video_lacks_fmtp); point them at stream_served_video_lacks_fmtp / the
  served SDP (same-change doc-sync).
- 'the main is always consumed by the recorder' is only true for the default
  record-from-main policy; soften in the go2rtc.rs docstring, the reconcile
  comment, and the DECISIONS correction (a record-from-sub main has no RTSP
  consumer -> served verdict None -> unrepaired, by design).
- add a reconcile debug log when the repair is enabled but a main has no
  served-SDP verdict, so 'enabled but silently does nothing' is observable.
- extra unit cases: webrtc-before-rtsp ordering, and a mid-handshake rtsp
  consumer (no sdp) skipped for the next one.

record-from-sub main-repair blindness and the sub-side incomplete-fmtp class are
tracked as follow-ups, not this PR.

Signed-off-by: badbread <badbread@users.noreply.github.com>
@badbread

badbread commented Aug 8, 2026

Copy link
Copy Markdown
Owner Author

Fable adversarial review: SHIP-WITH-FIXES, applied in the second commit.

  • Confirmed the load-bearing assumption on the live stack: the recorder is a persistent RTSP consumer of the main under the default record-from-main policy, so the served-SDP verdict is reliably present — the fix is not inert on prod.
  • Applied: doc-sync in config.rs/dto.rs (were still describing producer-side detection), softened the "always consumed by the recorder" claim (only true for record-from-main), added a reconcile debug log for "enabled but no served verdict", and two extra unit cases (webrtc-before-rtsp ordering; mid-handshake rtsp consumer skipped).
  • Deferred to main/sub fmtp-repair gaps surfaced by #592: record-from-sub blindness + sub-side incomplete fmtp #593 (pre-existing, not this PR): record-from-sub main-repair blindness, and the sub-side incomplete-fmtp class.

…no-verdict debug log

Addresses the Fable review of #592:
- config.rs / dto.rs doc comments still described PRODUCER-side detection
  (sdp_video_lacks_fmtp); point them at stream_served_video_lacks_fmtp / the
  served SDP (same-change doc-sync).
- 'the main is always consumed by the recorder' is only true for the default
  record-from-main policy; soften in the go2rtc.rs docstring, the reconcile
  comment, and the DECISIONS correction (a record-from-sub main has no RTSP
  consumer -> served verdict None -> unrepaired, by design).
- add a reconcile debug log when the repair is enabled but a main has no
  served-SDP verdict, so 'enabled but silently does nothing' is observable.
- extra unit cases: webrtc-before-rtsp ordering, and a mid-handshake rtsp
  consumer (no sdp) skipped for the next one.

record-from-sub main-repair blindness and the sub-side incomplete-fmtp class are
tracked as follow-ups, not this PR.

Signed-off-by: badbread <badbread@users.noreply.github.com>
@badbread
badbread force-pushed the fix/mainv-consumer-sdp-detection branch from 114a7d6 to 5ed4f24 Compare August 8, 2026 21:47
@badbread
badbread merged commit e194630 into main Aug 8, 2026
8 checks passed
@badbread
badbread deleted the fix/mainv-consumer-sdp-detection branch August 8, 2026 22:01
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