diff --git a/src/ConferenceCallDebugger.js b/src/ConferenceCallDebugger.js new file mode 100644 index 0000000..c95511d --- /dev/null +++ b/src/ConferenceCallDebugger.js @@ -0,0 +1,391 @@ +import EventEmitter from "events"; + +export class ConferenceCallDebugger extends EventEmitter { + constructor(manager) { + super(); + + this.manager = manager; + + this.debugState = { + users: new Map(), + calls: new Map(), + }; + + this.manager.on("call", this._onCall); + this.manager.on("debugstate", this._onDebugStateChanged); + this.manager.client.on("event", this._onEvent); + } + + _onEvent = (event) => { + const roomId = event.getRoomId(); + const type = event.getType(); + + if ( + roomId === this.manager.roomId && + (type.startsWith("m.call.") || type === "me.robertlong.call.info") + ) { + const sender = event.getSender(); + const { call_id } = event.getContent(); + + if (call_id) { + if (this.debugState.calls.has(call_id)) { + const callState = this.debugState.calls.get(call_id); + callState.events.push(event); + } else { + this.debugState.calls.set(call_id, { + state: "unknown", + events: [event], + }); + } + } + + if (this.debugState.users.has(sender)) { + const userState = this.debugState.users.get(sender); + userState.events.push(event); + } else { + this.debugState.users.set(sender, { + state: "unknown", + events: [event], + }); + } + + this.emit("debug"); + } + }; + + _onDebugStateChanged = (userId, callId, state) => { + if (userId) { + const userState = this.debugState.users.get(userId); + + if (userState) { + userState.state = state; + } else { + this.debugState.users.set(userId, { + state, + events: [], + }); + } + } + + if (callId) { + const callState = this.debugState.calls.get(callId); + + if (callState) { + callState.state = state; + } else { + this.debugState.calls.set(callId, { + state, + events: [], + }); + } + } + + this.emit("debug"); + }; + + _onCall = (call) => { + const peerConnection = call.peerConn; + + if (!peerConnection) { + return; + } + + const sendWebRTCInfoEvent = async (eventType) => { + const event = { + call_id: call.callId, + eventType, + iceConnectionState: peerConnection.iceConnectionState, + iceGatheringState: peerConnection.iceGatheringState, + signalingState: peerConnection.signalingState, + selectedCandidatePair: null, + localCandidate: null, + remoteCandidate: null, + }; + + // getStats doesn't support selectors in Firefox so get all stats by passing null. + // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats#browser_compatibility + const stats = await peerConnection.getStats(null); + + const statsArr = Array.from(stats.values()); + + // Matrix doesn't support floats so we convert time in seconds to ms + function secToMs(time) { + if (time === undefined) { + return undefined; + } + + return Math.round(time * 1000); + } + + function processTransportStats(transportStats) { + if (!transportStats) { + return undefined; + } + + return { + packetsSent: transportStats.packetsSent, + packetsReceived: transportStats.packetsReceived, + bytesSent: transportStats.bytesSent, + bytesReceived: transportStats.bytesReceived, + iceRole: transportStats.iceRole, + iceState: transportStats.iceState, + dtlsState: transportStats.dtlsState, + dtlsCipher: transportStats.dtlsCipher, + tlsVersion: transportStats.tlsVersion, + }; + } + + function processCandidateStats(candidateStats) { + if (!candidateStats) { + return undefined; + } + + // TODO: Figure out how to normalize ip and address across browsers + // networkType property excluded for privacy reasons: + // https://www.w3.org/TR/webrtc-stats/#sotd + return { + priority: + candidateStats.priority && candidateStats.priority.toString(), + candidateType: candidateStats.candidateType, + protocol: candidateStats.protocol, + address: !!candidateStats.address + ? candidateStats.address + : candidateStats.ip, + port: candidateStats.port, + url: candidateStats.url, + relayProtocol: candidateStats.relayProtocol, + }; + } + + function processCandidatePair(candidatePairStats) { + if (!candidatePairStats) { + return undefined; + } + + const localCandidateStats = statsArr.find( + (stat) => stat.id === candidatePairStats.localCandidateId + ); + event.localCandidate = processCandidateStats(localCandidateStats); + + const remoteCandidateStats = statsArr.find( + (stat) => stat.id === candidatePairStats.remoteCandidateId + ); + event.remoteCandidate = processCandidateStats(remoteCandidateStats); + + const transportStats = statsArr.find( + (stat) => stat.id === candidatePairStats.transportId + ); + event.transport = processTransportStats(transportStats); + + return { + state: candidatePairStats.state, + bytesSent: candidatePairStats.bytesSent, + bytesReceived: candidatePairStats.bytesReceived, + requestsSent: candidatePairStats.requestsSent, + requestsReceived: candidatePairStats.requestsReceived, + responsesSent: candidatePairStats.responsesSent, + responsesReceived: candidatePairStats.responsesReceived, + currentRoundTripTime: secToMs( + candidatePairStats.currentRoundTripTime + ), + totalRoundTripTime: secToMs(candidatePairStats.totalRoundTripTime), + }; + } + + // Firefox uses the deprecated "selected" property for the nominated ice candidate. + const selectedCandidatePair = statsArr.find( + (stat) => + stat.type === "candidate-pair" && (stat.selected || stat.nominated) + ); + + event.selectedCandidatePair = processCandidatePair(selectedCandidatePair); + + function processCodecStats(codecStats) { + if (!codecStats) { + return undefined; + } + + // Payload type enums and MIME types listed here: + // https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml + return { + mimeType: codecStats.mimeType, + clockRate: codecStats.clockRate, + payloadType: codecStats.payloadType, + channels: codecStats.channels, + sdpFmtpLine: codecStats.sdpFmtpLine, + }; + } + + function processRTPStreamStats(rtpStreamStats) { + const codecStats = statsArr.find( + (stat) => stat.id === rtpStreamStats.codecId + ); + const codec = processCodecStats(codecStats); + + return { + kind: rtpStreamStats.kind, + codec, + }; + } + + function processInboundRTPStats(inboundRTPStats) { + const rtpStreamStats = processRTPStreamStats(inboundRTPStats); + + return { + ...rtpStreamStats, + decoderImplementation: inboundRTPStats.decoderImplementation, + bytesReceived: inboundRTPStats.bytesReceived, + packetsReceived: inboundRTPStats.packetsReceived, + packetsLost: inboundRTPStats.packetsLost, + jitter: secToMs(inboundRTPStats.jitter), + frameWidth: inboundRTPStats.frameWidth, + frameHeight: inboundRTPStats.frameHeight, + frameBitDepth: inboundRTPStats.frameBitDepth, + framesPerSecond: + inboundRTPStats.framesPerSecond && + inboundRTPStats.framesPerSecond.toString(), + framesReceived: inboundRTPStats.framesReceived, + framesDecoded: inboundRTPStats.framesDecoded, + framesDropped: inboundRTPStats.framesDropped, + totalSamplesDecoded: inboundRTPStats.totalSamplesDecoded, + totalDecodeTime: secToMs(inboundRTPStats.totalDecodeTime), + totalProcessingDelay: secToMs(inboundRTPStats.totalProcessingDelay), + }; + } + + function processOutboundRTPStats(outboundRTPStats) { + const rtpStreamStats = processRTPStreamStats(outboundRTPStats); + + return { + ...rtpStreamStats, + encoderImplementation: outboundRTPStats.encoderImplementation, + bytesSent: outboundRTPStats.bytesSent, + packetsSent: outboundRTPStats.packetsSent, + frameWidth: outboundRTPStats.frameWidth, + frameHeight: outboundRTPStats.frameHeight, + frameBitDepth: outboundRTPStats.frameBitDepth, + framesPerSecond: + outboundRTPStats.framesPerSecond && + outboundRTPStats.framesPerSecond.toString(), + framesSent: outboundRTPStats.framesSent, + framesEncoded: outboundRTPStats.framesEncoded, + qualityLimitationReason: outboundRTPStats.qualityLimitationReason, + qualityLimitationResolutionChanges: + outboundRTPStats.qualityLimitationResolutionChanges, + totalEncodeTime: secToMs(outboundRTPStats.totalEncodeTime), + totalPacketSendDelay: secToMs(outboundRTPStats.totalPacketSendDelay), + }; + } + + function processRemoteInboundRTPStats(remoteInboundRTPStats) { + const rtpStreamStats = processRTPStreamStats(remoteInboundRTPStats); + + return { + ...rtpStreamStats, + packetsReceived: remoteInboundRTPStats.packetsReceived, + packetsLost: remoteInboundRTPStats.packetsLost, + jitter: secToMs(remoteInboundRTPStats.jitter), + framesDropped: remoteInboundRTPStats.framesDropped, + roundTripTime: secToMs(remoteInboundRTPStats.roundTripTime), + totalRoundTripTime: secToMs(remoteInboundRTPStats.totalRoundTripTime), + fractionLost: + remoteInboundRTPStats.fractionLost !== undefined && + remoteInboundRTPStats.fractionLost.toString(), + reportsReceived: remoteInboundRTPStats.reportsReceived, + roundTripTimeMeasurements: + remoteInboundRTPStats.roundTripTimeMeasurements, + }; + } + + function processRemoteOutboundRTPStats(remoteOutboundRTPStats) { + const rtpStreamStats = processRTPStreamStats(remoteOutboundRTPStats); + + return { + ...rtpStreamStats, + encoderImplementation: remoteOutboundRTPStats.encoderImplementation, + bytesSent: remoteOutboundRTPStats.bytesSent, + packetsSent: remoteOutboundRTPStats.packetsSent, + roundTripTime: secToMs(remoteOutboundRTPStats.roundTripTime), + totalRoundTripTime: secToMs( + remoteOutboundRTPStats.totalRoundTripTime + ), + reportsSent: remoteOutboundRTPStats.reportsSent, + roundTripTimeMeasurements: + remoteOutboundRTPStats.roundTripTimeMeasurements, + }; + } + + event.inboundRTP = statsArr + .filter((stat) => stat.type === "inbound-rtp") + .map(processInboundRTPStats); + + event.outboundRTP = statsArr + .filter((stat) => stat.type === "outbound-rtp") + .map(processOutboundRTPStats); + + event.remoteInboundRTP = statsArr + .filter((stat) => stat.type === "remote-inbound-rtp") + .map(processRemoteInboundRTPStats); + + event.remoteOutboundRTP = statsArr + .filter((stat) => stat.type === "remote-outbound-rtp") + .map(processRemoteOutboundRTPStats); + + this.manager.client.sendEvent( + this.manager.roomId, + "me.robertlong.call.info", + event + ); + }; + + let statsTimeout; + + const sendStats = () => { + if ( + call.state === "ended" || + peerConnection.connectionState === "closed" + ) { + clearTimeout(statsTimeout); + return; + } + + sendWebRTCInfoEvent("stats"); + statsTimeout = setTimeout(sendStats, 30 * 1000); + }; + + setTimeout(sendStats, 30 * 1000); + + peerConnection.addEventListener("iceconnectionstatechange", () => { + sendWebRTCInfoEvent("iceconnectionstatechange"); + }); + peerConnection.addEventListener("icegatheringstatechange", () => { + sendWebRTCInfoEvent("icegatheringstatechange"); + }); + peerConnection.addEventListener("negotiationneeded", () => { + sendWebRTCInfoEvent("negotiationneeded"); + }); + peerConnection.addEventListener("track", () => { + sendWebRTCInfoEvent("track"); + }); + // NOTE: Not available on Firefox + // https://bugzilla.mozilla.org/show_bug.cgi?id=1561441 + peerConnection.addEventListener( + "icecandidateerror", + ({ errorCode, url, errorText }) => { + this.manager.client.sendEvent( + this.manager.roomId, + "me.robertlong.call.ice_error", + { + call_id: call.callId, + errorCode, + url, + errorText, + } + ); + } + ); + peerConnection.addEventListener("signalingstatechange", () => { + sendWebRTCInfoEvent("signalingstatechange"); + }); + }; +} diff --git a/src/ConferenceCallManager.js b/src/ConferenceCallManager.js index a41caec..2a04a51 100644 --- a/src/ConferenceCallManager.js +++ b/src/ConferenceCallManager.js @@ -15,6 +15,7 @@ limitations under the License. */ import EventEmitter from "events"; +import { ConferenceCallDebugger } from "./ConferenceCallDebugger"; const CONF_ROOM = "me.robertlong.conf"; const CONF_PARTICIPANT = "me.robertlong.conf.participant"; @@ -144,14 +145,10 @@ export class ConferenceCallManager extends EventEmitter { }; this.participants = [this.localParticipant]; this.pendingCalls = []; - this.callUserMap = new Map(); - this.debugState = { - users: new Map(), - calls: new Map(), - }; - this.client.on("event", this._onEvent); + this.client.on("RoomState.members", this._onMemberChanged); this.client.on("Call.incoming", this._onIncomingCall); + this.callDebugger = new ConferenceCallDebugger(this); } setRoom(roomId) { @@ -166,7 +163,7 @@ export class ConferenceCallManager extends EventEmitter { this.joined = true; - this._setDebugState(this.client.getUserId(), null, "you"); + this.emit("debugstate", this.client.getUserId(), null, "you"); const activeConf = this.room.currentState .getStateEvents(CONF_ROOM, "") @@ -192,7 +189,7 @@ export class ConferenceCallManager extends EventEmitter { const userId = call.opponentMember.userId; this._addCall(call, userId); call.answer(); - this._observePeerConnection(call); + this.emit("call", call); } this.pendingCalls = []; @@ -200,73 +197,6 @@ export class ConferenceCallManager extends EventEmitter { this._updateParticipantState(); } - _onEvent = (event) => { - const roomId = event.getRoomId(); - const type = event.getType(); - - if ( - roomId === this.roomId && - (type.startsWith("m.call.") || type === "me.robertlong.call.info") - ) { - const sender = event.getSender(); - const { call_id } = event.getContent(); - - if (call_id) { - if (this.debugState.calls.has(call_id)) { - const callState = this.debugState.calls.get(call_id); - callState.events.push(event); - } else { - this.debugState.calls.set(call_id, { - state: "unknown", - events: [event], - }); - } - } - - if (this.debugState.users.has(sender)) { - const userState = this.debugState.users.get(sender); - userState.events.push(event); - } else { - this.debugState.users.set(sender, { - state: "unknown", - events: [event], - }); - } - - this.emit("debug"); - } - }; - - _setDebugState(userId, callId, state) { - if (userId) { - const userState = this.debugState.users.get(userId); - - if (userState) { - userState.state = state; - } else { - this.debugState.users.set(userId, { - state, - events: [], - }); - } - } - - if (callId) { - const callState = this.debugState.calls.get(callId); - - if (callState) { - callState.state = state; - } else { - this.debugState.calls.set(callId, { - state, - events: [], - }); - } - } - - this.emit("debug"); - } - _updateParticipantState = () => { const userId = this.client.getUserId(); const currentMemberState = this.room.currentState.getStateEvents( @@ -328,319 +258,21 @@ export class ConferenceCallManager extends EventEmitter { new Date().getTime() - participantTimeout > PARTICIPANT_TIMEOUT ) { // Member is inactive so don't call them. - this._setDebugState(userId, null, "inactive"); + this.emit("debugstate", userId, null, "inactive"); return; } // Only initiate a call with a user who has a userId that is lexicographically // less than your own. Otherwise, that user will call you. if (userId < localUserId) { - this._setDebugState(userId, null, "waiting for invite"); + this.emit("debugstate", userId, null, "waiting for invite"); return; } const call = this.client.createCall(this.roomId, userId); this._addCall(call, userId); call.placeVideoCall().then(() => { - this._observePeerConnection(call); - }); - } - - _observePeerConnection(call) { - const peerConnection = call.peerConn; - - if (!peerConnection) { - return; - } - - const sendWebRTCInfoEvent = async (eventType) => { - const event = { - call_id: call.callId, - eventType, - iceConnectionState: peerConnection.iceConnectionState, - iceGatheringState: peerConnection.iceGatheringState, - signalingState: peerConnection.signalingState, - selectedCandidatePair: null, - localCandidate: null, - remoteCandidate: null, - }; - - // getStats doesn't support selectors in Firefox so get all stats by passing null. - // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/getStats#browser_compatibility - const stats = await peerConnection.getStats(null); - - const statsArr = Array.from(stats.values()); - - // Matrix doesn't support floats so we convert time in seconds to ms - function secToMs(time) { - if (time === undefined) { - return undefined; - } - - return Math.round(time * 1000); - } - - function processTransportStats(transportStats) { - if (!transportStats) { - return undefined; - } - - return { - packetsSent: transportStats.packetsSent, - packetsReceived: transportStats.packetsReceived, - bytesSent: transportStats.bytesSent, - bytesReceived: transportStats.bytesReceived, - iceRole: transportStats.iceRole, - iceState: transportStats.iceState, - dtlsState: transportStats.dtlsState, - dtlsCipher: transportStats.dtlsCipher, - tlsVersion: transportStats.tlsVersion, - }; - } - - function processCandidateStats(candidateStats) { - if (!candidateStats) { - return undefined; - } - - // TODO: Figure out how to normalize ip and address across browsers - // networkType property excluded for privacy reasons: - // https://www.w3.org/TR/webrtc-stats/#sotd - return { - priority: - candidateStats.priority && candidateStats.priority.toString(), - candidateType: candidateStats.candidateType, - protocol: candidateStats.protocol, - address: !!candidateStats.address - ? candidateStats.address - : candidateStats.ip, - port: candidateStats.port, - url: candidateStats.url, - relayProtocol: candidateStats.relayProtocol, - }; - } - - function processCandidatePair(candidatePairStats) { - if (!candidatePairStats) { - return undefined; - } - - const localCandidateStats = statsArr.find( - (stat) => stat.id === candidatePairStats.localCandidateId - ); - event.localCandidate = processCandidateStats(localCandidateStats); - - const remoteCandidateStats = statsArr.find( - (stat) => stat.id === candidatePairStats.remoteCandidateId - ); - event.remoteCandidate = processCandidateStats(remoteCandidateStats); - - const transportStats = statsArr.find( - (stat) => stat.id === candidatePairStats.transportId - ); - event.transport = processTransportStats(transportStats); - - return { - state: candidatePairStats.state, - bytesSent: candidatePairStats.bytesSent, - bytesReceived: candidatePairStats.bytesReceived, - requestsSent: candidatePairStats.requestsSent, - requestsReceived: candidatePairStats.requestsReceived, - responsesSent: candidatePairStats.responsesSent, - responsesReceived: candidatePairStats.responsesReceived, - currentRoundTripTime: secToMs( - candidatePairStats.currentRoundTripTime - ), - totalRoundTripTime: secToMs(candidatePairStats.totalRoundTripTime), - }; - } - - // Firefox uses the deprecated "selected" property for the nominated ice candidate. - const selectedCandidatePair = statsArr.find( - (stat) => - stat.type === "candidate-pair" && (stat.selected || stat.nominated) - ); - - event.selectedCandidatePair = processCandidatePair(selectedCandidatePair); - - function processCodecStats(codecStats) { - if (!codecStats) { - return undefined; - } - - // Payload type enums and MIME types listed here: - // https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml - return { - mimeType: codecStats.mimeType, - clockRate: codecStats.clockRate, - payloadType: codecStats.payloadType, - channels: codecStats.channels, - sdpFmtpLine: codecStats.sdpFmtpLine, - }; - } - - function processRTPStreamStats(rtpStreamStats) { - const codecStats = statsArr.find( - (stat) => stat.id === rtpStreamStats.codecId - ); - const codec = processCodecStats(codecStats); - - return { - kind: rtpStreamStats.kind, - codec, - }; - } - - function processInboundRTPStats(inboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(inboundRTPStats); - - return { - ...rtpStreamStats, - decoderImplementation: inboundRTPStats.decoderImplementation, - bytesReceived: inboundRTPStats.bytesReceived, - packetsReceived: inboundRTPStats.packetsReceived, - packetsLost: inboundRTPStats.packetsLost, - jitter: secToMs(inboundRTPStats.jitter), - frameWidth: inboundRTPStats.frameWidth, - frameHeight: inboundRTPStats.frameHeight, - frameBitDepth: inboundRTPStats.frameBitDepth, - framesPerSecond: - inboundRTPStats.framesPerSecond && - inboundRTPStats.framesPerSecond.toString(), - framesReceived: inboundRTPStats.framesReceived, - framesDecoded: inboundRTPStats.framesDecoded, - framesDropped: inboundRTPStats.framesDropped, - totalSamplesDecoded: inboundRTPStats.totalSamplesDecoded, - totalDecodeTime: secToMs(inboundRTPStats.totalDecodeTime), - totalProcessingDelay: secToMs(inboundRTPStats.totalProcessingDelay), - }; - } - - function processOutboundRTPStats(outboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(outboundRTPStats); - - return { - ...rtpStreamStats, - encoderImplementation: outboundRTPStats.encoderImplementation, - bytesSent: outboundRTPStats.bytesSent, - packetsSent: outboundRTPStats.packetsSent, - frameWidth: outboundRTPStats.frameWidth, - frameHeight: outboundRTPStats.frameHeight, - frameBitDepth: outboundRTPStats.frameBitDepth, - framesPerSecond: - outboundRTPStats.framesPerSecond && - outboundRTPStats.framesPerSecond.toString(), - framesSent: outboundRTPStats.framesSent, - framesEncoded: outboundRTPStats.framesEncoded, - qualityLimitationReason: outboundRTPStats.qualityLimitationReason, - qualityLimitationResolutionChanges: - outboundRTPStats.qualityLimitationResolutionChanges, - totalEncodeTime: secToMs(outboundRTPStats.totalEncodeTime), - totalPacketSendDelay: secToMs(outboundRTPStats.totalPacketSendDelay), - }; - } - - function processRemoteInboundRTPStats(remoteInboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(remoteInboundRTPStats); - - return { - ...rtpStreamStats, - packetsReceived: remoteInboundRTPStats.packetsReceived, - packetsLost: remoteInboundRTPStats.packetsLost, - jitter: secToMs(remoteInboundRTPStats.jitter), - framesDropped: remoteInboundRTPStats.framesDropped, - roundTripTime: secToMs(remoteInboundRTPStats.roundTripTime), - totalRoundTripTime: secToMs(remoteInboundRTPStats.totalRoundTripTime), - fractionLost: - remoteInboundRTPStats.fractionLost !== undefined && - remoteInboundRTPStats.fractionLost.toString(), - reportsReceived: remoteInboundRTPStats.reportsReceived, - roundTripTimeMeasurements: - remoteInboundRTPStats.roundTripTimeMeasurements, - }; - } - - function processRemoteOutboundRTPStats(remoteOutboundRTPStats) { - const rtpStreamStats = processRTPStreamStats(remoteOutboundRTPStats); - - return { - ...rtpStreamStats, - encoderImplementation: remoteOutboundRTPStats.encoderImplementation, - bytesSent: remoteOutboundRTPStats.bytesSent, - packetsSent: remoteOutboundRTPStats.packetsSent, - roundTripTime: secToMs(remoteOutboundRTPStats.roundTripTime), - totalRoundTripTime: secToMs( - remoteOutboundRTPStats.totalRoundTripTime - ), - reportsSent: remoteOutboundRTPStats.reportsSent, - roundTripTimeMeasurements: - remoteOutboundRTPStats.roundTripTimeMeasurements, - }; - } - - event.inboundRTP = statsArr - .filter((stat) => stat.type === "inbound-rtp") - .map(processInboundRTPStats); - - event.outboundRTP = statsArr - .filter((stat) => stat.type === "outbound-rtp") - .map(processOutboundRTPStats); - - event.remoteInboundRTP = statsArr - .filter((stat) => stat.type === "remote-inbound-rtp") - .map(processRemoteInboundRTPStats); - - event.remoteOutboundRTP = statsArr - .filter((stat) => stat.type === "remote-outbound-rtp") - .map(processRemoteOutboundRTPStats); - - this.client.sendEvent(this.roomId, "me.robertlong.call.info", event); - }; - - let statsTimeout; - - const sendStats = () => { - if ( - call.state === "ended" || - peerConnection.connectionState === "closed" - ) { - clearTimeout(statsTimeout); - return; - } - - sendWebRTCInfoEvent("stats"); - statsTimeout = setTimeout(sendStats, 30 * 1000); - }; - - setTimeout(sendStats, 30 * 1000); - - peerConnection.addEventListener("iceconnectionstatechange", () => { - sendWebRTCInfoEvent("iceconnectionstatechange"); - }); - peerConnection.addEventListener("icegatheringstatechange", () => { - sendWebRTCInfoEvent("icegatheringstatechange"); - }); - peerConnection.addEventListener("negotiationneeded", () => { - sendWebRTCInfoEvent("negotiationneeded"); - }); - peerConnection.addEventListener("track", () => { - sendWebRTCInfoEvent("track"); - }); - // NOTE: Not available on Firefox - // https://bugzilla.mozilla.org/show_bug.cgi?id=1561441 - peerConnection.addEventListener( - "icecandidateerror", - ({ errorCode, url, errorText }) => { - this.client.sendEvent(this.roomId, "me.robertlong.call.ice_error", { - call_id: call.callId, - errorCode, - url, - errorText, - }); - } - ); - peerConnection.addEventListener("signalingstatechange", () => { - sendWebRTCInfoEvent("signalingstatechange"); + this.emit("call", call); }); } @@ -684,7 +316,7 @@ export class ConferenceCallManager extends EventEmitter { const userId = call.opponentMember.userId; this._addCall(call, userId); call.answer(); - this._observePeerConnection(call); + this.emit("call", call); }; _addCall(call, userId) { @@ -707,7 +339,7 @@ export class ConferenceCallManager extends EventEmitter { }); call.on("state", (state) => - this._setDebugState(userId, call.callId, state) + this.emit("debugstate", userId, call.callId, state) ); call.on("feeds_changed", () => this._onCallFeedsChanged(call)); call.on("hangup", () => this._onCallHangup(call)); @@ -765,7 +397,7 @@ export class ConferenceCallManager extends EventEmitter { ); remoteParticipant.call = newCall; - this._observePeerConnection(newCall); + this.emit("call", newCall); newCall.on("feeds_changed", () => this._onCallFeedsChanged(newCall)); newCall.on("hangup", () => this._onCallHangup(newCall)); diff --git a/src/DevTools.jsx b/src/DevTools.jsx index 9a3fc92..ff975dc 100644 --- a/src/DevTools.jsx +++ b/src/DevTools.jsx @@ -40,19 +40,19 @@ function sortEntries(a, b) { } export function DevTools({ manager }) { - const [debugState, setDebugState] = useState(manager.debugState); + const [debugState, setDebugState] = useState(manager.callDebugger.debugState); const [selectedEvent, setSelectedEvent] = useState(); const [activeTab, setActiveTab] = useState("users"); useEffect(() => { function onRoomDebug() { - setDebugState({ ...manager.debugState }); + setDebugState({ ...manager.callDebugger.debugState }); } - manager.on("debug", onRoomDebug); + manager.callDebugger.on("debug", onRoomDebug); return () => { - manager.removeListener("debug", onRoomDebug); + manager.callDebugger.removeListener("debug", onRoomDebug); }; }, [manager]);