UNPKG

@oiij/web-rtc

Version:
180 lines (179 loc) 4.84 kB
// src/index.ts import { onUnmounted, ref } from "vue"; function useWebRTC(options) { const { host = "127.0.0.1", port = 6789, path = "/_web-rtc", protocols = [], ...rtcConfig } = options ?? {}; const controller = new AbortController(); const id = ref(); const connected = ref([]); const status = ref("pending"); const ws = new WebSocket(`ws://${host}:${port}${path}`, protocols); const peer = new RTCPeerConnection(rtcConfig); const iceConnectionState = ref(peer.iceConnectionState); const signalingState = ref(peer.signalingState); const connectionState = ref(peer.connectionState); let onReadyFn = null; function onReady() { if (onReadyFn && typeof onReadyFn === "function") { onReadyFn(); } } let onConnectionFn = null; function onConnection(ev) { if (onConnectionFn && typeof onConnectionFn === "function") { onConnectionFn(ev); } } let onConnectionStreamFn = null; function onConnectionStream(ev) { if (onConnectionStreamFn && typeof onConnectionStreamFn === "function") { onConnectionStreamFn(ev); } } function sendMessage(data) { ws.send(JSON.stringify(data)); } function onIcecandidate(ev) { if (ev.candidate) { sendMessage({ type: "ice-candidate", payload: { candidate: ev.candidate } }); } } async function onMessage(ev) { try { const { type, payload } = JSON.parse(ev.data); const { key, desc, candidate } = payload; switch (type) { case "register": id.value = key; status.value = "ready"; onReady(); break; case "offer": { if (!desc || !key) return; await peer.setRemoteDescription(desc); const answer = await peer.createAnswer(); await peer.setLocalDescription(answer); sendMessage({ type: "answer", payload: { key, desc: answer } }); } break; case "answer": if (!desc || !key) return; await peer.setRemoteDescription(desc); sendMessage({ type: "answer-ok", payload: { key } }); break; case "answer-ok": if (!key) { return; } status.value = "connected"; if (!connected.value.includes(key)) { connected.value.push(key); } break; case "ice-candidate": if (!candidate) return; try { await peer.addIceCandidate(candidate); } catch (error) { console.error(error); } break; default: break; } } catch (error) { console.error(error); } } async function connect(id2, label = "label") { const dataChannel = peer.createDataChannel(label); const offer = await peer.createOffer(); await peer.setLocalDescription(offer); sendMessage({ type: "offer", payload: { key: id2, desc: offer } }); return dataChannel; } async function connectStream(id2, stream) { stream.getTracks().forEach((track) => { peer.addTrack(track, stream); }); const offer = await peer.createOffer(); await peer.setLocalDescription(offer); sendMessage({ type: "offer", payload: { key: id2, desc: offer } }); return peer; } ws.addEventListener("message", onMessage, { signal: controller.signal }); peer.addEventListener("icecandidate", onIcecandidate, { signal: controller.signal }); peer.addEventListener("iceconnectionstatechange", () => { iceConnectionState.value = peer.iceConnectionState; }, { signal: controller.signal }); peer.addEventListener("signalingstatechange", () => { signalingState.value = peer.signalingState; }, { signal: controller.signal }); peer.addEventListener("connectionstatechange", () => { connectionState.value = peer.connectionState; }, { signal: controller.signal }); peer.addEventListener("datachannel", onConnection, { signal: controller.signal }); peer.addEventListener("track", onConnectionStream, { signal: controller.signal }); function destroy() { controller.abort(); ws.close(); peer.close(); } onUnmounted(() => { destroy(); }); return { id, connected, status, ws, peer, signalingState, connectionState, onReady: (fn) => { onReadyFn = fn; }, connect, onConnection: (fn) => { onConnectionFn = fn; }, connectStream, onConnectionStream: (fn) => { onConnectionStreamFn = fn; } }; } export { useWebRTC };