Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F7313561
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
35 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/call.js b/lib/call.js
index 0d9429a..2f5b8cb 100644
--- a/lib/call.js
+++ b/lib/call.js
@@ -1,371 +1,405 @@
'use strict';
import debug from 'debug';
import uuidv4 from 'uuid/v4';
import utils from './utils';
import { EventEmitter } from 'events';
const DEBUG = debug('sylkrtc:Call');
class Call extends EventEmitter {
constructor(account) {
super();
this._account = account;
this._id = null;
this._direction = null;
this._pc = null;
this._state = null;
this._terminated = false;
this._incomingSdp = null;
this._localIdentity = new utils.Identity(account.id, account.displayName);
this._remoteIdentity = null;
+ this._remoteStreams = new MediaStream();
+ this._localStreams = new MediaStream();
this._dtmfSender = null;
this._delay_established = false; // set to true when we need to delay posting the state change to 'established'
this._setup_in_progress = false; // set while we set the remote description and setup the peer copnnection
// bind some handlers to this instance
this._onDtmf = this._onDtmf.bind(this);
}
get account() {
return this._account;
}
get id() {
return this._id;
}
get direction() {
return this._direction;
}
get state() {
return this._state;
}
get localIdentity() {
return this._localIdentity;
}
get remoteIdentity() {
return this._remoteIdentity;
}
getLocalStreams() {
if (this._pc !== null) {
- return this._pc.getLocalStreams();
+ if (this._pc.getSenders) {
+ this._pc.getSenders().forEach((e) => {
+ this._localStreams.addTrack(e.track);
+ });
+ return [this._localStreams];
+ } else {
+ return this._pc.getLocalStreams();
+ }
} else {
return [];
}
}
getRemoteStreams() {
if (this._pc !== null) {
- return this._pc.getRemoteStreams();
+ if (this._pc.getReceivers) {
+ this._pc.getReceivers().forEach((e) => {
+ this._remoteStreams.addTrack(e.track);
+ });
+ return [this._remoteStreams];
+ } else {
+ return this._pc.getRemoteStreams();
+ }
} else {
return [];
}
}
answer(options = {}) {
if (this._state !== 'incoming') {
throw new Error('Call is not in the incoming state: ' + this._state);
}
if (!options.localStream) {
throw new Error('Missing localStream');
}
const pcConfig = options.pcConfig || {iceServers:[]};
const answerOptions = options.answerOptions;
// Create the RTCPeerConnection
this._initRTCPeerConnection(pcConfig);
this._pc.addStream(options.localStream);
this.emit('localStreamAdded', options.localStream);
this._pc.setRemoteDescription(
new RTCSessionDescription({type: 'offer', sdp: this._incomingSdp}),
// success
() => {
utils.createLocalSdp(this._pc, 'answer', answerOptions)
.then((sdp) => {
DEBUG('Local SDP: %s', sdp);
this._sendAnswer(sdp);
})
.catch((reason) => {
DEBUG(reason);
this.terminate();
});
},
// failure
(error) => {
DEBUG('Error setting remote description: %s', error);
this.terminate();
}
);
}
terminate() {
if (this._terminated) {
return;
}
DEBUG('Terminating call');
this._sendTerminate();
}
sendDtmf(tones, duration=100, interToneGap=70) {
DEBUG('sendDtmf()');
if (this._dtmfSender === null) {
if (this._pc !== null) {
let track = null;
try {
track = this._pc.getLocalStreams()[0].getAudioTracks()[0];
} catch (e) {
// ignore
}
if (track !== null) {
DEBUG('Creating DTMF sender');
this._dtmfSender = this._pc.createDTMFSender(track);
if (this._dtmfSender) {
this._dtmfSender.addEventListener('tonechange', this._onDtmf);
}
}
}
}
if (this._dtmfSender) {
DEBUG('Sending DTMF tones');
this._dtmfSender.insertDTMF(tones, duration, interToneGap);
}
}
// Private API
_initOutgoing(uri, options={}) {
if (uri.indexOf('@') === -1) {
throw new Error('Invalid URI');
}
if (!options.localStream) {
throw new Error('Missing localStream');
}
this._id = uuidv4();
this._direction = 'outgoing';
this._remoteIdentity = new utils.Identity(uri);
const pcConfig = options.pcConfig || {iceServers:[]};
const offerOptions = options.offerOptions;
// Create the RTCPeerConnection
this._initRTCPeerConnection(pcConfig);
this._pc.addStream(options.localStream);
this.emit('localStreamAdded', options.localStream);
utils.createLocalSdp(this._pc, 'offer', offerOptions)
.then((sdp) => {
DEBUG('Local SDP: %s', sdp);
this._sendCall(uri, sdp);
})
.catch((reason) => {
DEBUG(reason);
this._localTerminate(reason);
});
}
_initIncoming(id, caller, sdp) {
this._id = id;
this._remoteIdentity = new utils.Identity(caller.uri, caller.display_name);
this._incomingSdp = sdp;
this._direction = 'incoming';
this._state = 'incoming';
DEBUG('Remote SDP: %s', sdp);
}
_handleEvent(message) {
DEBUG('Call event: %o', message);
switch (message.event) {
case 'state':
let oldState = this._state;
let newState = message.state;
this._state = newState;
if (newState === 'accepted' && this._direction === 'outgoing') {
DEBUG('Call accepted');
this.emit('stateChanged', oldState, newState, {});
const sdp = utils.mungeSdp(message.sdp);
DEBUG('Remote SDP: %s', sdp);
this._setup_in_progress = true;
this._pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: sdp}),
// success
() => {
this._setup_in_progress = false;
if (!this._terminated) {
if (this._delay_established) {
oldState = this._state;
this._state = 'established';
DEBUG('Setting delayed established state!');
this.emit('stateChanged', oldState, this._state, {});
this._delay_established = false;
}
}
},
// failure
(error) => {
DEBUG('Error accepting call: %s', error);
this.terminate();
}
);
} else if (newState === 'established' && this._direction === 'outgoing') {
if (this._setup_in_progress) {
this._delay_established = true;
} else {
this.emit('stateChanged', oldState, newState, {});
}
} else if (newState === 'terminated') {
this.emit('stateChanged', oldState, newState, {reason: message.reason});
this._terminated = true;
this._account._calls.delete(this.id);
this._closeRTCPeerConnection();
} else {
this.emit('stateChanged', oldState, newState, {});
}
break;
default:
break;
}
}
_initRTCPeerConnection(pcConfig) {
if (this._pc !== null) {
throw new Error('RTCPeerConnection already initialized');
}
this._pc = new RTCPeerConnection(pcConfig);
this._pc.addEventListener('addstream', (event) => {
DEBUG('Stream added');
this.emit('streamAdded', event.stream);
});
this._pc.addEventListener('icecandidate', (event) => {
if (event.candidate !== null) {
DEBUG('New ICE candidate %o', event.candidate);
} else {
DEBUG('ICE candidate gathering finished');
}
this._sendTrickle(event.candidate);
});
}
_sendRequest(req, cb) {
this._account._sendRequest(req, cb);
}
_sendCall(uri, sdp) {
const req = {
sylkrtc: 'session-create',
account: this.account.id,
session: this.id,
uri: uri,
sdp: sdp
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Call error: %s', error);
this._localTerminate(error);
}
});
}
_sendTerminate() {
const req = {
sylkrtc: 'session-terminate',
session: this.id
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Error terminating call: %s', error);
this._localTerminate(error);
}
});
setTimeout(() => {
if (!this._terminated) {
DEBUG('Timeout terminating call');
this._localTerminate('200 OK');
}
this._terminated = true;
}, 150);
}
_sendTrickle(candidate) {
const req = {
sylkrtc: 'session-trickle',
session: this.id,
candidates: candidate !== null ? [candidate] : [],
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Trickle error: %s', error);
this._localTerminate(error);
}
});
}
_sendAnswer(sdp) {
const req = {
sylkrtc: 'session-answer',
session: this.id,
sdp: sdp
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Answer error: %s', error);
this.terminate();
}
});
}
_closeRTCPeerConnection() {
DEBUG('Closing RTCPeerConnection');
if (this._pc !== null) {
- for (let stream of this._pc.getLocalStreams()) {
- utils.closeMediaStream(stream);
+ let tempStream;
+ if (this._pc.getSenders) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getSenders()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getLocalStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
- for (let stream of this._pc.getRemoteStreams()) {
- utils.closeMediaStream(stream);
+
+ if (this._pc.getReceivers) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getReceivers()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getRemoteStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
this._pc.close();
this._pc = null;
if (this._dtmfSender !== null) {
this._dtmfSender.removeEventListener('tonechange', this._onDtmf);
this._dtmfSender = null;
}
}
}
_localTerminate(error) {
if (this._terminated) {
return;
}
DEBUG('Local terminate');
this._account._calls.delete(this.id);
this._terminated = true;
const oldState = this._state;
const newState = 'terminated';
const data = {
reason: error.toString()
};
this._closeRTCPeerConnection();
this.emit('stateChanged', oldState, newState, data);
}
_onDtmf(event) {
DEBUG('Sent DTMF tone %s', event.tone);
this.emit('dtmfToneSent', event.tone);
}
}
export { Call };
diff --git a/lib/conference.js b/lib/conference.js
index 648c2b7..b1a888b 100644
--- a/lib/conference.js
+++ b/lib/conference.js
@@ -1,635 +1,687 @@
'use strict';
import debug from 'debug';
import uuidv4 from 'uuid/v4';
import utils from './utils';
import { EventEmitter } from 'events';
const DEBUG = debug('sylkrtc:Conference');
class Participant extends EventEmitter {
constructor(publisherId, identity, conference) {
super();
this._id = uuidv4();
this._publisherId = publisherId;
this._identity = identity;
this._conference = conference;
this._state = null;
this._pc = null;
+ this._stream = new MediaStream();
this._videoSubscriptionPaused = false;
this._audioSubscriptionPaused = false;
this._videoPublishingPaused = false;
this._audioPublishingPaused = false;
}
get id() {
return this._id;
}
get publisherId() {
return this._publisherId;
}
get identity() {
return this._identity;
}
get conference() {
return this._conference;
}
get videoPaused() {
return this._videoSubscriptionPaused;
}
get state() {
return this._state;
}
get streams() {
if (this._pc !== null) {
- return this._pc.getRemoteStreams();
+ if (this._pc.getReceivers) {
+ this._pc.getReceivers().forEach((e) => {
+ this._stream.addTrack(e.track);
+ });
+ return [this._stream];
+ } else {
+ return this._pc.getRemoteStreams();
+ }
} else {
return [];
}
}
attach() {
if (this._state !== null) {
return;
}
this._setState('progress');
this._sendAttach();
}
detach() {
if (this._state !== null) {
this._sendDetach();
}
}
pauseVideo() {
this._sendUpdate({video: false});
this._videoSubscriptionPaused = true;
}
resumeVideo() {
this._sendUpdate({video: true});
this._videoSubscriptionPaused = false;
}
_setState(newState) {
const oldState = this._state;
this._state = newState;
DEBUG(`Participant ${this.id} state change: ${oldState} -> ${newState}`);
this.emit('stateChanged', oldState, newState);
}
_handleOffer(offerSdp) {
DEBUG('Handling SDP for participant offer: %s', offerSdp);
// Create the RTCPeerConnection
const pcConfig = this.conference._pcConfig;
const pc = new RTCPeerConnection(pcConfig);
pc.addEventListener('addstream', (event) => {
DEBUG('Stream added');
this.emit('streamAdded', event.stream);
});
pc.addEventListener('icecandidate', (event) => {
if (event.candidate !== null) {
DEBUG('New ICE candidate %o', event.candidate);
} else {
DEBUG('ICE candidate gathering finished');
}
this._sendTrickle(event.candidate);
});
this._pc = pc;
// no need for a local stream since we are only going to receive media here
pc.setRemoteDescription(
new RTCSessionDescription({type: 'offer', sdp: offerSdp}),
// success
() => {
utils.createLocalSdp(pc, 'answer')
.then((sdp) => {
DEBUG('Local SDP: %s', sdp);
this._sendAnswer(sdp);
})
.catch((reason) => {
DEBUG(reason);
this._close();
});
},
// failure
(error) => {
DEBUG('Error setting remote description: %s', error);
this._close();
}
);
}
_sendAttach() {
const req = {
sylkrtc: 'videoroom-feed-attach',
session: this.conference.id,
publisher: this._publisherId,
feed: this.id
};
DEBUG('Sending request: %o', req);
this.conference._sendRequest(req, (error) => {
if (error) {
DEBUG('Error attaching to participant %s: %s', this._publisherId, error);
}
});
}
_sendDetach() {
const req = {
sylkrtc: 'videoroom-feed-detach',
session: this.conference.id,
feed: this.id
};
DEBUG('Sending request: %o', req);
this.conference._sendRequest(req, (error) => {
if (error) {
DEBUG('Error detaching to participant %s: %s', this._publisherId, error);
}
this._close();
});
}
_sendTrickle(candidate) {
const req = {
sylkrtc: 'videoroom-session-trickle',
session: this.id,
candidates: candidate !== null ? [candidate] : []
};
this.conference._sendRequest(req, (error) => {
if (error) {
DEBUG('Trickle error: %s', error);
this._close();
}
});
}
_sendAnswer(sdp) {
const req = {
sylkrtc: 'videoroom-feed-answer',
session: this.conference.id,
feed: this.id,
sdp: sdp
};
DEBUG('Sending request: %o', req);
this.conference._sendRequest(req, (error) => {
if (error) {
DEBUG('Answer error: %s', error);
this._close();
}
});
}
_sendUpdate(options = {}) {
const req = {
sylkrtc: 'videoroom-session-update',
session: this.id,
options: options
};
DEBUG('Sending update participant request %o', req);
this.conference._sendRequest(req, (error) => {
if (error) {
DEBUG('Answer error: %s', error);
}
});
}
_close() {
DEBUG('Closing Participant RTCPeerConnection');
if (this._pc !== null) {
- for (let stream of this._pc.getLocalStreams()) {
- utils.closeMediaStream(stream);
+ let tempStream;
+ if (this._pc.getSenders) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getSenders()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getLocalStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
- for (let stream of this._pc.getRemoteStreams()) {
- utils.closeMediaStream(stream);
+
+ if (this._pc.getReceivers) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getReceivers()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getRemoteStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
this._pc.close();
this._pc = null;
this._setState(null);
}
}
}
class ConferenceCall extends EventEmitter {
constructor(account) {
super();
this._account = account;
this._id = null;
this._pc = null;
this._participants = new Map();
this._terminated = false;
this._state = null;
this._localIdentity = new utils.Identity(account.id, account.displayName);
+ this._localStreams = new MediaStream();
this._remoteIdentity = null;
this._activeParticpants = [];
this._pcConfig = null; // saved on initialize, used later for subscriptions
this._delay_established = false; // set to true when we need to delay posting the state change to 'established'
this._setup_in_progress = false; // set while we set the remote description and setup the peer copnnection
}
get account() {
return this._account;
}
get id() {
return this._id;
}
get direction() {
// make this object API compatible with `Call`
return 'outgoing';
}
get state() {
return this._state;
}
get localIdentity() {
return this._localIdentity;
}
get remoteIdentity() {
return this._remoteIdentity;
}
get participants() {
return Array.from(new Set(this._participants.values()));
}
get activeParticipants() {
return this._activeParticpants;
}
getLocalStreams() {
if (this._pc !== null) {
- return this._pc.getLocalStreams();
+ if (this._pc.getSenders) {
+ this._pc.getSenders().forEach((e) => {
+ this._localStreams.addTrack(e.track);
+ });
+ return [this._localStreams];
+ } else {
+ return this._pc.getLocalStreams();
+ }
} else {
return [];
}
}
getRemoteStreams() {
let streams = [];
for (let participant of new Set(this._participants.values())) {
streams = streams.concat(participant.streams);
}
return streams;
}
scaleLocalTrack(oldTrack, divider) {
DEBUG('Scaling track by %d', divider);
let sender;
for (sender of this._pc.getSenders()) {
if (sender.track === oldTrack) {
DEBUG('Found sender to modify track %o', sender);
break;
}
}
sender.setParameters({encodings: [{scaleResolutionDownBy: divider}]})
.then(() => {
DEBUG("Scale set to %o", divider);
DEBUG('Active encodings %o', sender.getParameters().encodings);
})
.catch((error) => {
DEBUG('Error %o', error);
});
}
configureRoom(ps, cb=null) {
if (!Array.isArray(ps)) {
return;
}
this._sendConfigureRoom(ps, cb);
}
terminate() {
if (this._terminated) {
return;
}
DEBUG('Terminating conference');
this._sendTerminate();
}
inviteParticipants(ps) {
if (this._terminated) {
return;
}
if (!Array.isArray(ps) || ps.length === 0) {
return;
}
DEBUG('Inviting participants: %o', ps);
const req = {
sylkrtc: 'videoroom-invite',
session: this.id,
participants: ps
};
this._sendRequest(req, null);
}
// Private API
_initialize(uri, options={}) {
if (this._id !== null) {
throw new Error('Already initialized');
}
if (uri.indexOf('@') === -1) {
throw new Error('Invalid URI');
}
if (!options.localStream) {
throw new Error('Missing localStream');
}
this._id = uuidv4();
this._remoteIdentity = new utils.Identity(uri);
options = Object.assign({}, options);
const pcConfig = options.pcConfig || {iceServers:[]};
this._pcConfig = pcConfig;
this._initialParticipants = options.initialParticipants || [];
const offerOptions = options.offerOptions || {};
// only send audio / video through the publisher connection
offerOptions.offerToReceiveAudio = false;
offerOptions.offerToReceiveVideo = false;
delete offerOptions.mandatory;
// Create the RTCPeerConnection
this._pc = new RTCPeerConnection(pcConfig);
this._pc.addEventListener('icecandidate', (event) => {
if (event.candidate !== null) {
DEBUG('New ICE candidate %o', event.candidate);
} else {
DEBUG('ICE candidate gathering finished');
}
this._sendTrickle(event.candidate);
});
this._pc.addStream(options.localStream);
this.emit('localStreamAdded', options.localStream);
DEBUG('Offer options: %o', offerOptions);
utils.createLocalSdp(this._pc, 'offer', offerOptions)
.then((sdp) => {
DEBUG('Local SDP: %s', sdp);
this._sendJoin(sdp);
})
.catch((reason) => {
this._localTerminate(reason);
});
}
_handleEvent(message) {
DEBUG('Conference event: %o', message);
let participant;
switch (message.event) {
case 'session-state':
let oldState = this._state;
let newState = message.state;
this._state = newState;
if (newState === 'accepted') {
this.emit('stateChanged', oldState, newState, {});
const sdp = utils.mungeSdp(message.sdp);
DEBUG('Remote SDP: %s', sdp);
this._setup_in_progress = true;
this._pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: sdp}),
// success
() => {
this._setup_in_progress = false;
if (!this._terminated) {
if (this._delay_established) {
oldState = this._state;
this._state = 'established';
DEBUG('Setting delayed established state!');
this.emit('stateChanged', oldState, this._state, {});
this._delay_established = false;
}
DEBUG('Conference accepted');
if (this._initialParticipants.length > 0 ) {
setTimeout(() => {
this.inviteParticipants(this._initialParticipants);
}, 50);
}
}
},
// failure
(error) => {
DEBUG('Error processing conference accept: %s', error);
this.terminate();
}
);
} else if (newState === 'established') {
if (this._setup_in_progress) {
this._delay_established = true;
} else {
this.emit('stateChanged', oldState, newState, {});
}
} else if (newState === 'terminated') {
this.emit('stateChanged', oldState, newState, {reason: message.reason});
this._terminated = true;
this._close();
} else {
this.emit('stateChanged', oldState, newState, {});
}
break;
case 'initial-publishers':
// this comes between 'accepted' and 'established' states
for (let p of message.publishers) {
participant = new Participant(p.id, new utils.Identity(p.uri, p.display_name), this);
this._participants.set(participant.id, participant);
this._participants.set(p.id, participant);
}
break;
case 'publishers-joined':
for (let p of message.publishers) {
DEBUG('Participant joined: %o', p);
participant = new Participant(p.id, new utils.Identity(p.uri, p.display_name), this);
this._participants.set(participant.id, participant);
this._participants.set(p.id, participant);
this.emit('participantJoined', participant);
}
break;
case 'publishers-left':
for (let pId of message.publishers) {
participant = this._participants.get(pId);
if (participant) {
this._participants.delete(participant.id);
this._participants.delete(pId);
this.emit('participantLeft', participant);
}
}
break;
case 'feed-attached':
participant = this._participants.get(message.feed);
if (participant) {
participant._handleOffer(message.sdp);
}
break;
case 'feed-established':
participant = this._participants.get(message.feed);
if (participant) {
participant._setState('established');
}
break;
case 'configure':
let activeParticipants = [];
let originator;
const mappedOriginator = this._participants.get(message.originator);
if (mappedOriginator) {
originator = mappedOriginator.identity;
} else if (message.originator === this.id) {
originator = this.localIdentity;
} else if (message.originator === 'videoroom'){
originator = message.originator;
}
for (let pId of message.active_participants) {
participant = this._participants.get(pId);
if (participant) {
activeParticipants.push(participant);
} else if (pId === this.id) {
activeParticipants.push({
id: this.id,
publisherId: this.id,
identity: this.localIdentity,
streams: this.getLocalStreams()
});
}
}
this._activeParticpants = activeParticipants;
const roomConfig = {originator: originator, activeParticipants: this._activeParticpants};
this.emit('roomConfigured', roomConfig);
break;
default:
break;
}
}
_sendConfigureRoom(ps, cb = null) {
const req = {
sylkrtc: 'videoroom-configure',
session: this.id,
active_participants: ps
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Error configuring room: %s', error);
if (cb) {
cb(error);
}
} else {
DEBUG('Configure room send: %o', ps);
}
});
}
_sendJoin(sdp) {
const req = {
sylkrtc: 'videoroom-join',
account: this.account.id,
session: this.id,
uri: this.remoteIdentity.uri,
sdp: sdp
};
DEBUG('Sending request: %o', req);
this._sendRequest(req, (error) => {
if (error) {
this._localTerminate(error);
}
});
}
_sendTerminate() {
const req = {
sylkrtc: 'videoroom-leave',
session: this.id
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Error terminating conference: %s', error);
this._localTerminate(error);
}
});
setTimeout(() => {
if (!this._terminated) {
DEBUG('Timeout terminating call');
this._localTerminate('');
}
this._terminated = true;
}, 150);
}
_sendTrickle(candidate) {
const req = {
sylkrtc: 'videoroom-session-trickle',
session: this.id,
candidates: candidate !== null ? [candidate] : []
};
this._sendRequest(req, (error) => {
if (error) {
DEBUG('Trickle error: %s', error);
this._localTerminate(error);
}
});
}
_sendRequest(req, cb) {
this._account._sendRequest(req, cb);
}
_close() {
DEBUG('Closing RTCPeerConnection');
if (this._pc !== null) {
- for (let stream of this._pc.getLocalStreams()) {
- utils.closeMediaStream(stream);
+ let tempStream;
+ if (this._pc.getSenders) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getSenders()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getLocalStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
- for (let stream of this._pc.getRemoteStreams()) {
- utils.closeMediaStream(stream);
+
+ if (this._pc.getReceivers) {
+ tempStream = new MediaStream();
+ for (let track of this._pc.getReceivers()) {
+ tempStream.addTrack(track.track);
+ }
+ utils.closeMediaStream(tempStream);
+ } else {
+ for (let stream of this._pc.getRemoteStreams()) {
+ utils.closeMediaStream(stream);
+ }
}
this._pc.close();
this._pc = null;
}
const participants = this.participants;
this._participants = [];
for (let p of participants) {
p._close();
}
}
_localTerminate(reason) {
if (this._terminated) {
return;
}
DEBUG(`Local terminate, reason: ${reason}`);
this._account._confCalls.delete(this.id);
this._terminated = true;
const oldState = this._state;
const newState = 'terminated';
const data = {
reason: reason.toString()
};
this._close();
this.emit('stateChanged', oldState, newState, data);
}
}
export { ConferenceCall };
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Feb 1, 7:01 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3489463
Default Alt Text
(35 KB)
Attached To
Mode
rSYLKRTC SylkRTC.js
Attached
Detach File
Event Timeline
Log In to Comment