From 101a67382768610dcc03473f1e94f5264b3b0b0a Mon Sep 17 00:00:00 2001 From: kim Date: Sat, 19 Sep 2026 23:55:22 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20K2=20=EC=B9=B4=EB=A9=94=EB=9D=BC=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20=EC=A7=80=EC=97=B0(20~30=EC=B4=88)=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=E2=80=94=20ICE=20gathering=20=EC=99=84?= =?UTF-8?q?=EC=A0=84=EC=A2=85=EB=A3=8C=20=EB=8C=80=EC=8B=A0=201.5=EC=B4=88?= =?UTF-8?q?=20=EB=B0=94=EC=9A=B4=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit onicecandidate가 ev.candidate===null(전체 gathering 종료)까지 기다린 뒤에만 오퍼를 보내고 있었는데, 이 네트워크에서 그 신호가 20~30초씩 걸림(가상 인터페이스들 순회+ 타임아웃으로 추정). 실제 필요한 STUN srflx 후보는 1~2초면 잡히므로, 완전종료를 기다리지 않고 1.5초 타임아웃 뒤 그때까지 모인 후보로 바로 오퍼를 보내도록 변경. 브라우저 실측: 연결까지 20~30초 → 3초 이내로 단축. --- web-ui/html/k2-app.html | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/web-ui/html/k2-app.html b/web-ui/html/k2-app.html index 318976b..de60b84 100644 --- a/web-ui/html/k2-app.html +++ b/web-ui/html/k2-app.html @@ -91,22 +91,33 @@ function connectCam(){ setStatus('끊김'); } }; + // 원래는 ev.candidate===null(ICE gathering 완전 종료)까지 기다린 뒤에만 오퍼를 + // 보냈는데, 이 네트워크에서 그 완전종료 신호가 20~30초씩 걸림(가상/VPN 인터페이스들을 + // 하나씩 훑고 타임아웃되는 걸로 보임) — 정작 이 프린터가 필요로 하는 STUN srflx + // 후보 자체는 보통 1~2초 안에 잡힘. 그래서 완전종료를 기다리지 않고 짧은 타임아웃 + // 뒤에 그때까지 모인 후보로 바로 보낸다(2026-09-19, 20초+ 연결지연 확인 후 수정). + let offerSent=false; + function sendOfferOnce(){ + if(offerSent || pc!==camPC) return; + offerSent=true; + fetch('/api/k2/webrtc-signal',{ + method:'POST', + headers:{'Content-Type':'text/plain', ...authH()}, + body:btoa(JSON.stringify({type:'offer',sdp:pc.localDescription.sdp})), + }).then(r=>r.text()).then(txt=>{ + const res=JSON.parse(atob(txt)); + if(res.type==='answer' && pc===camPC){ + pc.setRemoteDescription(new RTCSessionDescription(res)); + } + }).catch(()=>{ if(pc===camPC) setStatus('연결 실패'); }); + } pc.onicecandidate=(ev)=>{ - if(ev.candidate===null){ - fetch('/api/k2/webrtc-signal',{ - method:'POST', - headers:{'Content-Type':'text/plain', ...authH()}, - body:btoa(JSON.stringify({type:'offer',sdp:pc.localDescription.sdp})), - }).then(r=>r.text()).then(txt=>{ - const res=JSON.parse(atob(txt)); - if(res.type==='answer' && pc===camPC){ - pc.setRemoteDescription(new RTCSessionDescription(res)); - } - }).catch(()=>{ if(pc===camPC) setStatus('연결 실패'); }); - } + if(ev.candidate===null) sendOfferOnce(); // 빨리 끝나면 그대로 씀 }; pc.addTransceiver('video',{direction:'sendrecv'}); - pc.createOffer().then(d=>pc.setLocalDescription(d)).catch(()=>{ if(pc===camPC) setStatus('연결 실패'); }); + pc.createOffer().then(d=>pc.setLocalDescription(d)).then(()=>{ + setTimeout(sendOfferOnce, 1500); // 안 끝나면 1.5초 뒤 그때까지 모인 후보로 진행 + }).catch(()=>{ if(pc===camPC) setStatus('연결 실패'); }); } function reconnectCam(){ connectCam(); } connectCam();