UNPKG

dpg.broadcast-webrtc

Version:
227 lines (197 loc) 7.12 kB
import * as IO from "socket.io-client"; import type { Socket as SocketType, SocketOptions, ManagerOptions, } from "socket.io-client"; interface SocketData { readonly socket: SocketType; readonly routes?: { [eventName: string]: (...any) => any | Promise<any>; }; } interface SocketDataOwner extends SocketData { children_db?: { [socketId: string]: RTCPeerConnection }; } interface SocketConfig extends Partial<IO.ManagerOptions & IO.SocketOptions> { cid: string; } class PeerConnectionVisitor implements SocketData { private peer: RTCPeerConnection; private answer: RTCSessionDescriptionInit; readonly routes: SocketData["routes"] = { "peerconnection/visitor/offer": async ( socketId: SocketType["id"], offer: RTCSessionDescriptionInit, ) => { console.log("GET OFFER", { socketId, kek: this.socket.id, bool: socketId == this.socket.id }) if (socketId == this.socket.id) { await this.initOffer(offer); await this.initAnswer(); } }, }; readonly socket: SocketType; protected async initOffer(offer: RTCSessionDescriptionInit) { await this.peer.setRemoteDescription(offer); this.peer.restartIce(); console.log("[PeerConnection] Status:", { signal: this.peer.signalingState, connection: this.peer.connectionState }) } protected async initAnswer() { this.answer = await this.peer.createAnswer(); await this.peer.setLocalDescription(this.answer); this.socket.emit("peerconnection/visitor/answer", { answer: this.answer, }); this.peer.restartIce(); console.log("[PeerConnection] Status:", { signal: this.peer.signalingState, connection: this.peer.connectionState }) } onTrack(callback: any) { this.peer.ontrack = ev => { callback(ev.streams[0]) } } constructor( rtcConfig: RTCConfiguration, socketConfig: SocketConfig | SocketType, ) { this.peer = new RTCPeerConnection(rtcConfig); this.socket = !!(socketConfig as SocketType)?.io ? (socketConfig as SocketType) : IO.connect( socketConfig as Partial< IO.ManagerOptions & IO.SocketOptions >, ); Object.keys(this.routes).forEach((eventKey) => this.socket.on(eventKey, this.routes[eventKey]), ); this.peer.onicecandidate = (ev) => ev?.candidate && this.socket.emit("peerconnection/visitor/candidate", { candidate: ev?.candidate, }); // this.peer.addEventListener("") this.peer.ondatachannel = (ev) => console.log("[PeerConnection] Channel open."); this.peer.onicecandidateerror = ev => { console.log("[PeerConnection] Error from Ice Candidate.", ev); } } } class PeerConnectionOwner implements SocketDataOwner { private peer: RTCPeerConnection; private readonly RTCConfiguration: RTCConfiguration; private readonly Stream: MediaStream; readonly socket: SocketType; readonly routes: SocketDataOwner["routes"] = { "peerconnection/owner/join": (socketId: SocketType["id"]) => { this.join(socketId); }, "peerconnection/owner/answer": ( socketId: SocketType["id"], answer: RTCSessionDescriptionInit, ) => { this.setAnswer(socketId, answer); }, "peerconnection/owner/candidate": ( socketId: SocketType["id"], candidate: RTCIceCandidate, ) => { this.setCandidate(socketId, candidate); }, }; private children: SocketDataOwner["children_db"] = {}; private async join(socketId: SocketType["id"]) { const peer = (this.children[socketId] = new RTCPeerConnection( this.RTCConfiguration, )); peer.createDataChannel("broadcast"); this.Stream.getTracks().forEach((track) => peer.addTrack(track, this.Stream), ); const offer = await peer.createOffer({iceRestart: true}); await peer.setLocalDescription(offer); this.socket.emit("peerconnection/owner/offer", socketId, offer); peer.restartIce(); console.log("[PeerConnection] Status:", { signal: peer.signalingState, connection: peer.connectionState }) } private setAnswer( socketId: SocketType["id"], answer: RTCSessionDescriptionInit, ) { this.children[socketId] .setRemoteDescription(answer) .then(candidate => console.log("[PeerConnection] Add answer")) .catch(candidate => console.log("[PeerConnection] Error answer", candidate)); } private setCandidate( socketId: SocketType["id"], candidate: RTCIceCandidate, ) { this.children[socketId].addIceCandidate( new RTCIceCandidate(candidate), ) .then(candidate => console.log("[PeerConnection] Add candidate")) .catch(candidate => console.log("[PeerConnection] Error candidate", candidate)); } constructor( rtcConfig: RTCConfiguration, socketConfig: SocketConfig | SocketType, stream: MediaStream, ) { this.peer = new RTCPeerConnection(rtcConfig); this.socket = !!(socketConfig as SocketType)?.io ? (socketConfig as SocketType) : IO.connect( socketConfig as Partial< IO.ManagerOptions & IO.SocketOptions >, ); Object.keys(this.routes).forEach((eventKey) => this.socket.on(eventKey, this.routes[eventKey]), ); this.RTCConfiguration = rtcConfig; this.Stream = stream; } } function init( config: RTCConfiguration, socketConfig: SocketConfig | SocketType, ): PeerConnectionVisitor; function init( config: RTCConfiguration, socketConfig: SocketConfig | SocketType, owner: false, stream?: undefined, ): PeerConnectionVisitor; function init( config: RTCConfiguration, socketConfig: SocketConfig | SocketType, owner: true, stream: MediaStream | any, ): PeerConnectionOwner; function init( config: RTCConfiguration, socketConfig: SocketConfig | SocketType, owner?: boolean, stream?: MediaStream | any, ) { if (owner) return new PeerConnectionOwner(config, socketConfig, stream); else return new PeerConnectionVisitor(config, socketConfig); } export { PeerConnectionVisitor, PeerConnectionOwner }; export default init;