zebra-report: surface boot errors + watchFirstFrame keyframe diagnostic
Mirror of zebra-report 3ca9042 + proxy.unturf.com bc04362. - moderation: boot failures now log to the moderator's page instead of looking like the button did nothing (stale uuid case). - diagnostic: watchFirstFrame fires 'still black after 2500ms — no keyframe?' if a fresh SFU video tile never decodes a first frame. - proxy-side companion: longer keyframe-burst on subscriber attach so a dropped PLI no longer leaves the tile permanently black.
This commit is contained in:
parent
19922ee9fa
commit
e09a1793b9
5 changed files with 51 additions and 15 deletions
|
|
@ -507,9 +507,9 @@ handle /zebra-spaces-sfu* { reverse_proxy localhost:8092 { flush_interval -1
|
|||
</p>
|
||||
|
||||
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">d66163119d2ed906b6b5860492cafe48</span><br>
|
||||
sha256 <span class="stamp-sha">6625f870ff5246e12c4c953eb6b96aa469de48f432f7c33eaa39247318a038b3</span><br>
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">24217bcab41f06970625faecdc0723e2</span><br>
|
||||
sha256 <span class="stamp-sha">9dda09c0e6405bd71a611c12722235ba1188cd11679575763a3c67d708ff3cc5</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=</span>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -341,9 +341,9 @@ try {
|
|||
</p>
|
||||
|
||||
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">4ace619f2fc1bf734a9fa1f31a7c3ad0</span><br>
|
||||
sha256 <span class="stamp-sha">6122621e01623b4a5af42ecad202cb9edfac5e8a81f51e217c1d925118042392</span><br>
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">e84a8aec0fd1558d63501f776929d883</span><br>
|
||||
sha256 <span class="stamp-sha">5d5258d99c37a5a8236bdb779971735f1c59d53ec0154e69dd406673f44c9a89</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -1747,9 +1747,9 @@ logLine('sys', 'chat content lives in encrypted SRTP audio. no IP packets carry
|
|||
})();
|
||||
</script>
|
||||
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">8d32e9b88a8f874b2e847d511cc55faf</span><br>
|
||||
sha256 <span class="stamp-sha">9d9e3f109e90eebef43bc0babe04f21541d3e797c182db48b166eecf5a35ed91</span><br>
|
||||
<span style="color:#777">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">15478b0cdc5ae94c6ddc9c625443058f</span><br>
|
||||
sha256 <span class="stamp-sha">793f3811124cba596f54f48ae11fab805ecf670991669002542b8ee8fdefc6f6</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -747,9 +747,9 @@ else wirePuppet();
|
|||
</section>
|
||||
|
||||
<footer style="max-width:560px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">6a7530604a70dd9e615906f3ae61e7a4</span><br>
|
||||
sha256 <span class="stamp-sha">41f32dda18b77c7cbf15ad02fc708822b278d0a2ceccc6f147dbb88495c947ca</span><br>
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">e9a723dc188a32c1cebccc01bdaa3191</span><br>
|
||||
sha256 <span class="stamp-sha">822e6a4cba5887f70af6b8b4350bb87c5599292ff019f6f24f89672195a2725d</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
|
|
@ -1702,6 +1702,38 @@ function watchVideoTrackForRemoval(track, removeFn, windowMs){
|
|||
track.addEventListener('unmute', onUnmute);
|
||||
}
|
||||
|
||||
/* watchFirstFrame — log a warning if a freshly-attached SFU video track
|
||||
* never unmutes within the deadline. Fresh remote tracks start `muted`
|
||||
* until the first RTP packet arrives; if the decoder never gets a
|
||||
* keyframe (PLI dropped in UDP, publisher encoder stalled, codec
|
||||
* mismatch), the tile renders permanently black with no clear signal in
|
||||
* the log. This emits one line so we can tell next session which path
|
||||
* actually broke. No state change — purely diagnostic. */
|
||||
function watchFirstFrame(track, kind, pubHex){
|
||||
if (!track) return;
|
||||
if (!track.muted){
|
||||
/* already flowing — nothing to wait for */
|
||||
return;
|
||||
}
|
||||
const deadline = 2500;
|
||||
const start = Date.now();
|
||||
let done = false;
|
||||
const settle = (note) => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
track.removeEventListener('unmute', onUnmute);
|
||||
if (note) logLine('', note);
|
||||
};
|
||||
const onUnmute = () => settle('first frame: kind=' + kind + ' pub=' + pubHex + ' in ' + (Date.now()-start) + 'ms');
|
||||
track.addEventListener('unmute', onUnmute);
|
||||
setTimeout(() => {
|
||||
if (done) return;
|
||||
if (track.readyState !== 'live'){ settle(null); return; }
|
||||
if (!track.muted){ settle('first frame: kind=' + kind + ' pub=' + pubHex + ' (unmute event missed)'); return; }
|
||||
settle('err: ' + kind + ' from ' + pubHex + ' still black after ' + deadline + 'ms — no keyframe?');
|
||||
}, deadline);
|
||||
}
|
||||
|
||||
/* Spotlight model — every tile keeps its thumbnail permanently in the
|
||||
* cameras column (cameras above, screens-thumbs below). When a tile is
|
||||
* spotlit, a SECOND larger tile renders in the middle slot pointing at
|
||||
|
|
@ -2505,6 +2537,7 @@ function handleRemoteSfuTrack(ev){
|
|||
screenStreams.set(pubHex, s);
|
||||
renderScreenTile(pubHex, s);
|
||||
watchVideoTrackForRemoval(ev.track, () => { if (screenStreams.get(pubHex) === s) removeScreenTile(pubHex); }, VIDEO_REMOVE_MUTE_WINDOW_SCREEN_MS);
|
||||
watchFirstFrame(ev.track, kind, pubHex);
|
||||
return;
|
||||
}
|
||||
if (kind === 'camera'){
|
||||
|
|
@ -2512,6 +2545,7 @@ function handleRemoteSfuTrack(ev){
|
|||
cameraStreams.set(pubHex, s);
|
||||
renderCameraTile(pubHex, s);
|
||||
watchVideoTrackForRemoval(ev.track, () => { if (cameraStreams.get(pubHex) === s) removeCameraTile(pubHex); }, VIDEO_REMOVE_MUTE_WINDOW_MS);
|
||||
watchFirstFrame(ev.track, kind, pubHex);
|
||||
return;
|
||||
}
|
||||
if (kind === 'game'){
|
||||
|
|
@ -2522,6 +2556,7 @@ function handleRemoteSfuTrack(ev){
|
|||
gameStreams.set(pubHex, s);
|
||||
renderVideoTile('gameshare', pubHex, s);
|
||||
watchVideoTrackForRemoval(ev.track, () => { if (gameStreams.get(pubHex) === s) removeVideoTile('gameshare', pubHex); }, VIDEO_REMOVE_MUTE_WINDOW_SCREEN_MS);
|
||||
watchFirstFrame(ev.track, kind, pubHex);
|
||||
return;
|
||||
}
|
||||
if (kind !== 'mic'){
|
||||
|
|
@ -3728,7 +3763,8 @@ function renderRoom(){
|
|||
/* boot allowed against anyone except host; cohosts also can't boot cohosts (host only) */
|
||||
if (m.role !== 'host' && !(m.role === 'cohost' && myRole !== 'host')){
|
||||
const b = document.createElement('button'); b.className='small';
|
||||
b.textContent = 'boot'; b.onclick = () => modBoot(m.uuid);
|
||||
b.textContent = 'boot';
|
||||
b.onclick = () => modBoot(m.uuid).catch(e => logLine('err','boot: '+e.message));
|
||||
acts.appendChild(b);
|
||||
}
|
||||
}
|
||||
|
|
@ -4171,8 +4207,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">af6e33ca6e69e36517ff3d2717849309</span><br>
|
||||
sha256 <span class="stamp-sha">0c94c5ebdb46bd0954888beed0e1f4c5bc75cdbab516e7f1bb72bbfbf0bba84f</span><br>
|
||||
md5 <span class="stamp-md5">c874915d93ed4b044f8a9fd30e542d7c</span><br>
|
||||
sha256 <span class="stamp-sha">e13bfc8b82cd58097e12ac00591b4f2a324f589aaae10f9b488bbe78e6cce82d</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue