minimal streamplace frontend
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 65 lines 1.8 kB view raw
1function waitForIceGathering(pc: RTCPeerConnection, timeout: number): Promise<void> { 2 return new Promise((resolve) => { 3 if (pc.iceGatheringState === "complete") { 4 resolve(); 5 return; 6 } 7 const timer = setTimeout(() => resolve(), timeout); 8 pc.onicegatheringstatechange = () => { 9 if (pc.iceGatheringState === "complete") { 10 clearTimeout(timer); 11 resolve(); 12 } 13 }; 14 }); 15} 16 17export interface WhepConnection { 18 pc: RTCPeerConnection; 19 stream: MediaStream; 20} 21 22export async function connectWhep(handle: string): Promise<WhepConnection> { 23 const whepUrl = `https://stream.place/api/playback/${encodeURIComponent(handle)}/webrtc?rendition=source`; 24 25 const pc = new RTCPeerConnection({ 26 iceServers: [{ urls: "stun:stun.l.google.com:19302" }], 27 bundlePolicy: "max-bundle", 28 }); 29 30 const stream = new MediaStream(); 31 32 pc.addTransceiver("video", { direction: "recvonly" }); 33 pc.addTransceiver("audio", { direction: "recvonly" }); 34 35 pc.ontrack = (event) => { 36 if (event.streams && event.streams[0]) { 37 for (const track of event.streams[0].getTracks()) { 38 stream.addTrack(track); 39 } 40 } else { 41 stream.addTrack(event.track); 42 } 43 }; 44 45 const offer = await pc.createOffer(); 46 await pc.setLocalDescription(offer); 47 await waitForIceGathering(pc, 500); 48 49 const resp = await fetch(whepUrl, { 50 method: "POST", 51 headers: { "Content-Type": "application/sdp" }, 52 body: pc.localDescription!.sdp, 53 }); 54 55 if (!resp.ok) { 56 pc.close(); 57 const errText = await resp.text(); 58 throw new Error(`WHEP ${resp.status}: ${errText}`); 59 } 60 61 const answerSdp = await resp.text(); 62 await pc.setRemoteDescription({ type: "answer", sdp: answerSdp }); 63 64 return { pc, stream }; 65}