From 63852195fc69f4c22575c7a1a97990d49f4a20ab Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 19:04:44 +0000 Subject: [PATCH] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/lib/webrtc/viewer.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/lib/webrtc/viewer.ts b/src/lib/webrtc/viewer.ts index fd0d329..87ab1b3 100644 --- a/src/lib/webrtc/viewer.ts +++ b/src/lib/webrtc/viewer.ts @@ -1,6 +1,7 @@ import { joinSignalingChannel, sendSignal, + waitForSignalingReady, ICE_SERVERS, type SignalMessage, } from './signaling'; @@ -17,6 +18,7 @@ export async function joinAsViewer( code: string, videoEl: HTMLVideoElement, onState: (s: 'connecting' | 'waiting' | 'live' | 'error', detail?: string) => void, + onFallbackFrame?: (dataUrl: string) => void, ): Promise { const viewerId = crypto.randomUUID(); let pc: RTCPeerConnection | null = null; @@ -25,18 +27,24 @@ export async function joinAsViewer( onState('connecting'); const handleMessage = async (msg: SignalMessage) => { + if (msg.type === 'frame') { + onFallbackFrame?.(msg.dataUrl); + onState('live'); + return; + } + if (msg.type === 'offer' && msg.viewerId === viewerId) { pc = new RTCPeerConnection({ iceServers: ICE_SERVERS }); pc.ontrack = (ev) => { - videoEl.srcObject = ev.streams[0]; + videoEl.srcObject = ev.streams[0] ?? new MediaStream([ev.track]); videoEl.play().catch(() => {/* autoplay may need user gesture */}); onState('live'); }; pc.onicecandidate = (ev) => { if (ev.candidate && channel) { - sendSignal(channel, { + void sendSignal(channel, { type: 'ice', viewerId, from: 'viewer', @@ -54,7 +62,7 @@ export async function joinAsViewer( await pc.setRemoteDescription(msg.sdp); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); - if (channel) sendSignal(channel, { type: 'answer', viewerId, sdp: answer }); + if (channel) await sendSignal(channel, { type: 'answer', viewerId, sdp: answer }); } else if (msg.type === 'ice' && msg.viewerId === viewerId && msg.from === 'broadcaster') { if (pc) { try { @@ -67,18 +75,14 @@ export async function joinAsViewer( }; channel = joinSignalingChannel(code, handleMessage); + await waitForSignalingReady(channel); - // Wait a tick for subscribe, then announce join - setTimeout(() => { - if (channel) { - sendSignal(channel, { type: 'viewer-join', viewerId }); - onState('waiting'); - } - }, 600); + await sendSignal(channel, { type: 'viewer-join', viewerId }); + onState('waiting'); const stop = async () => { if (channel) { - try { sendSignal(channel, { type: 'viewer-leave', viewerId }); } catch {} + try { await sendSignal(channel, { type: 'viewer-leave', viewerId }); } catch {} const { supabase } = await import('@/integrations/supabase/client'); await supabase.removeChannel(channel); }