Ignore late shards from completed Muninn frames
This commit is contained in:
parent
79781abdbb
commit
02c615670a
3 changed files with 43 additions and 13 deletions
|
|
@ -67,6 +67,7 @@ constexpr uint64_t MuninnReceiverKeyframeRequestCooldownMs = 500;
|
|||
constexpr uint64_t MuninnReceiverChunkRepairFirstWaitMs = 64;
|
||||
constexpr uint64_t MuninnVideoAssemblyExpiryScanMs = 10;
|
||||
constexpr size_t MuninnExpiredVideoFrameRememberCount = 4096;
|
||||
constexpr size_t MuninnCompletedVideoFrameRememberCount = 4096;
|
||||
constexpr size_t MuninnVideoParityCacheMaxEntries = 4096;
|
||||
constexpr uint16_t MuninnCatalogDiscoveryPort = 17874;
|
||||
constexpr uint32_t MuninnCatalogDiscoveryConnectionId = 0x6d750003;
|
||||
|
|
@ -1515,15 +1516,15 @@ private:
|
|||
log_bridge_pressure("dropped invalid video chunk metadata");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (media.chunk_count == 1) {
|
||||
return media.payload;
|
||||
}
|
||||
|
||||
const std::string key =
|
||||
media.stream_id + ":" + media.session_id + ":" + std::to_string(media.frame_id);
|
||||
if (expired_video_frame_keys_.find(key) != expired_video_frame_keys_.end()) {
|
||||
if (video_frame_is_settled(key)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (media.chunk_count == 1) {
|
||||
remember_completed_video_frame(key);
|
||||
return media.payload;
|
||||
}
|
||||
auto &assembly = video_assemblies_[key];
|
||||
if (assembly.chunks.empty()) {
|
||||
assembly.stream_id = media.stream_id;
|
||||
|
|
@ -1579,7 +1580,7 @@ private:
|
|||
}
|
||||
const std::string key =
|
||||
media.stream_id + ":" + media.session_id + ":" + std::to_string(media.frame_id);
|
||||
if (expired_video_frame_keys_.find(key) != expired_video_frame_keys_.end()) {
|
||||
if (video_frame_is_settled(key)) {
|
||||
++video_parity_ignored_;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -1683,9 +1684,27 @@ private:
|
|||
}
|
||||
video_assemblies_.erase(key);
|
||||
video_parity_cache_.erase(key);
|
||||
remember_completed_video_frame(key);
|
||||
return assembled;
|
||||
}
|
||||
|
||||
bool video_frame_is_settled(const std::string &key) const
|
||||
{
|
||||
return expired_video_frame_keys_.find(key) != expired_video_frame_keys_.end() ||
|
||||
completed_video_frame_keys_.find(key) != completed_video_frame_keys_.end();
|
||||
}
|
||||
|
||||
void remember_completed_video_frame(const std::string &key)
|
||||
{
|
||||
if (completed_video_frame_keys_.insert(key).second) {
|
||||
completed_video_frame_order_.push_back(key);
|
||||
}
|
||||
while (completed_video_frame_order_.size() > MuninnCompletedVideoFrameRememberCount) {
|
||||
completed_video_frame_keys_.erase(completed_video_frame_order_.front());
|
||||
completed_video_frame_order_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_recover_video_assembly_from_parity(MuninnVideoFrameAssembly &assembly)
|
||||
{
|
||||
if (assembly.parity_count == 0 ||
|
||||
|
|
@ -2037,6 +2056,8 @@ private:
|
|||
std::map<std::string, std::vector<MuninnVideoParityCacheEntry>> video_parity_cache_;
|
||||
std::deque<std::string> expired_video_frame_order_;
|
||||
std::set<std::string> expired_video_frame_keys_;
|
||||
std::deque<std::string> completed_video_frame_order_;
|
||||
std::set<std::string> completed_video_frame_keys_;
|
||||
muninn_rudp_fragments::FragmentAssembler packet_fragments_;
|
||||
muninn_rudp_fragments::FragmentAssembler audio_packet_fragments_;
|
||||
muninn_rudp_ack::AckTracker ack_tracker_;
|
||||
|
|
|
|||
|
|
@ -197,3 +197,4 @@
|
|||
{"ts": "2026-07-17T01:37:22+01:00", "type": "verification", "status": "partial", "note": "Muninn video parity v3 uses 16 Cauchy GF(256) shards; native receiver burst tests 1..16 and Odin 180-test suite pass. Direct Raven LAN at 250 ms recovery deadline ran over one minute with no video assembly expiry or A/V failure; full 10-minute and impairment matrix remain open."}
|
||||
{"ts": "2026-07-17T01:46:28+01:00", "type": "scar", "status": "fixed", "note": "Per-byte Gaussian elimination for production-sized video FEC can stall the single OBS receive owner and cascade socket loss. Receiver now inverts coefficients once per frame and uses table-driven GF(256); active Release assertions prove every 1..16 consecutive erasure case on 64x1400-byte shards under 50 ms."}
|
||||
{"ts": "2026-07-17T01:59:33+01:00", "type": "scar", "status": "open", "note": "Direct Raven LAN falsified trailing-parity sufficiency: a 24-datagram sequence gap left a 56-chunk keyframe at 52/56 with parity_present=false because all 16 V3 parity shards followed the data and fell in the same tail burst. Next cut must disperse parity/adapt block layout; deadline increases and repair flooding are not owners."}
|
||||
{"ts": "2026-07-17T02:34:05+01:00", "type": "verification", "status": "ok", "note": "Receiver completed-frame tombstones eliminated false post-FEC ghost assemblies: a 7-minute LAN run forwarded 54,000 payloads with zero video assembly expiry before an experimental weighted sender scheduler independently caused audio queue eviction; the scheduler was deleted. A later genuine 24-datagram tail burst left a frame 18/33 with only the leading 8 of 16 bookended parity shards available, proving V3 frame-wide bookends still need Moonlight-style multi-block FEC."}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ invariants:
|
|||
|
||||
accepted_design:
|
||||
- Muninn video FEC is now typed as `muninn.media_video_parity_shard.v3`.
|
||||
Odin emits 16 Cauchy GF(256) parity shards after each multi-chunk access
|
||||
Odin emits 16 Cauchy GF(256) parity shards around each multi-chunk access
|
||||
unit, so any erasure set up to the received parity count is recoverable;
|
||||
Mimir's OBS receiver owns the matching bounded matrix recovery before the
|
||||
assembly deadline. V2 stripe parity is never reinterpreted as V3. Native
|
||||
|
|
@ -97,12 +97,20 @@ accepted_design:
|
|||
recovery bound. Odin's 180 Muninn tests pass. A per-byte elimination draft
|
||||
was rejected after one recovery stalled the receiver thread; production
|
||||
inverts the coefficient matrix once per frame and uses table-driven field
|
||||
arithmetic. Direct Raven LAN evidence then exposed a remaining layout fault:
|
||||
a 24-datagram tail burst erased four final data chunks plus all sixteen
|
||||
trailing parity shards, so no parity survived to repair the otherwise small
|
||||
data loss. Repair retransmission is now capped behind fresh media and video
|
||||
datagrams use a 50 microsecond spin pacer, but parity dispersion/adaptive
|
||||
block sizing remains required before the clean ten-minute gate can pass.
|
||||
arithmetic. Direct Raven LAN evidence exposed two distinct faults. First,
|
||||
successful early FEC completion did not tombstone the frame, so late
|
||||
original chunks resurrected a partial ghost assembly and manufactured loss
|
||||
feedback; the receiver now remembers completed frame keys and ignores that
|
||||
residue. A seven-minute proof then kept every video failure counter at zero
|
||||
while recovering 12,504 frames. Second, a genuine 24-datagram tail burst
|
||||
left one frame at 18/33 chunks: the leading parity bookend supplied only
|
||||
eight equations and the trailing eight were lost with fifteen data shards.
|
||||
Repair retransmission is capped behind fresh media, IDR requests are
|
||||
coalesced to 500 ms, and video datagrams use a 50 microsecond spin pacer,
|
||||
but Moonlight-style independently recoverable FEC blocks remain required
|
||||
before the clean ten-minute gate can pass. An experimental weighted audio/
|
||||
video scheduler was deleted after its accumulated audio debt caused bounded
|
||||
queue eviction; audio retains strict latency priority.
|
||||
Normal frames are forwarded immediately; the recovery deadline is not a
|
||||
playout buffer. The ten-minute and impaired acceptance gates remain open.
|
||||
- Muninn now closes receiver congestion pressure into the live NVENC context.
|
||||
|
|
|
|||
Loading…
Reference in a new issue