const servers = { iceServers: [ { url: 'stun:stun01.sipphone.com' }, { url: 'stun:stun.ekiga.net' }, { url: 'stun:stun.fwdnet.net' }, { url: 'stun:stun.ideasip.com' }, { url: 'stun:stun.iptel.org' }, { url: 'stun:stun.rixtelecom.se' }, { url: 'stun:stun.schlund.de' }, { url: 'stun:stun.l.google.com:19302' }, { url: 'stun:stun1.l.google.com:19302' }, { url: 'stun:stun2.l.google.com:19302' }, { url: 'stun:stun3.l.google.com:19302' }, { url: 'stun:stun4.l.google.com:19302' }, { url: 'stun:stunserver.org' }, { url: 'stun:stun.softjoys.com' }, { url: 'stun:stun.voiparound.com' }, { url: 'stun:stun.voipbuster.com' }, { url: 'stun:stun.voipstunt.com' }, { url: 'stun:stun.voxgratia.org' }, { url: 'stun:stun.xten.com' }, // { // url: 'turn:numb.viagenie.ca', // credential: 'muazkh', // username: 'webrtc@live.com', // }, // { // url: 'turn:192.158.29.39:3478?transport=udp', // credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=', // username: '28224511:1379330808', // }, // { // url: 'turn:192.158.29.39:3478?transport=tcp', // credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=', // username: '28224511:1379330808', // }, ], }; const pcConstraints = { optional: [{ DtlsSrtpKeyAgreement: true }], }; export interface RTCPeerProps { channelName: string; offer?: RTCSessionDescriptionInit; sendAnswer: (offer: RTCSessionDescriptionInit) => void; sendOffer: (offer: RTCSessionDescriptionInit) => void; } export default class RTCPeer { channelName: string; peerConnection: RTCPeerConnection; channel: RTCDataChannel; sendAnswer: (offer: RTCSessionDescriptionInit) => void; sendOffer: (offer: RTCSessionDescriptionInit) => void; constructor({ channelName, offer, sendAnswer, sendOffer }: RTCPeerProps) { this.sendOffer = sendOffer; this.sendAnswer = sendAnswer; this.channelName = channelName; this.peerConnection = new RTCPeerConnection(); //(servers, pcConstraints); this.peerConnection.onicecandidate = offer ? this.#handleIceCandidateAnswer : this.#handleIceCandidateOffer; this.#createDataChannel(); if (offer) { console.log('answer'); this.peerConnection.setRemoteDescription(offer); this.peerConnection.createAnswer().then((answer) => { this.peerConnection.setLocalDescription(answer); }); } else { console.log('call'); this.peerConnection.createOffer().then((offer) => { this.peerConnection.setLocalDescription(offer); }); } } onAnswer = (answer: RTCSessionDescriptionInit) => { this.peerConnection.setRemoteDescription(answer); }; #handleIceCandidateAnswer = (event: RTCPeerConnectionIceEvent) => { if (!event.candidate) { const answer = this.peerConnection.localDescription; console.log('send-answer', { answer }); if (answer) { this.sendAnswer(answer); } } }; #handleIceCandidateOffer = (event: RTCPeerConnectionIceEvent) => { if (!event.candidate) { const offer = this.peerConnection.localDescription; if (offer) { console.log('send-offer', { offer }); this.sendOffer(offer); } } }; #createDataChannel = () => { try { this.channel = this.peerConnection.createDataChannel(this.channelName); this.channel.onopen = () => { console.log('Receive Channel[onopen]:', this.channel.readyState); }; this.channel.onmessage = (event: MessageEvent) => { console.log('Receive Channel[onmessage]:', event.data); }; this.channel.onclose = () => { console.log('Receive Channel[onclose]:', this.channel.readyState); }; } catch (error) { console.error('[RTCPeer|#createDataChannel] ERROR', error); } }; }