podchat-browser
Version:
Javascript SDK to use POD's Chat Service - Browser Only
1,122 lines (994 loc) • 43.2 kB
JavaScript
import {WebrtcPeerConnection} from "./webrtcPeer";
import Utility from "../../../utility/utility";
import {errorList} from "../../errorHandler";
import MidManager from "./midManager";
class PeerConnectionManager {
constructor({
app,
callId,
direction,
rtcPeerConfig,
brokerAddress,
onPeerFailed,
onSendTrackFailed
}) {
this._app = app;
this._callId = callId;
this._brokerAddress = brokerAddress;
this._nextTrackMid = 0;
this._trackList = [];
this._addTrackQueue = [];
this._sendTrackQueue = [];
this._addIceQueue = [];
this._direction = direction;
this._firstSub = true;
this._canProcessNextTrack = true;
this._isDestroyed = false;
this._requestTimeouts = {};
this._negotiationOfferUpdate = [];
this._isReconnecting = false;
this._subscribeOrUpdateFailedCount = 0;
this._peerStates = {
DISCONNECTED: 0,
CONNECTING: 1,
FAILED: 3,
CONNECTED: 4
};
this._state = 0; //0: disconnected, 1: connecting, 2: failed, 3: connected, 4: disconnected
this._defaultConfig = {
callId,
direction,
rtcPeerConfig,
connectionStateChange: this._onConnectionStateChange.bind(this),
iceConnectionStateChange: this._onIceConnectionStateChange.bind(this)
};
this._onPeerFailed = onPeerFailed;
this._onSendTrackFailed = onSendTrackFailed;
this._peer = new WebrtcPeerConnection(this._defaultConfig);
this._onTrackCalls = 0;
this._sendOpenDone = false;
this._sendOpenDoneTimeout = null;
this.watchConnectionStateChange();
this._addTrackLock = false;
this._addTrackQueueTriggerTimeout = null;
this._midManager = new MidManager({app});
let that = this;
window.trackList = function () {
return that._trackList;
}
}
_nextTrack(retries = 0) {
if(this.isDestroyed())
return;
if (this._canProcessNextTrack) {
if (this._direction == 'send' && this._canProcessNextTrack && this._addTrackQueue.length) {
if(this._firstSub || this._peer && this._peer.peerConnection && this._peer.peerConnection.connectionState == 'connected') {
this._canProcessNextTrack = false;
let item = this._addTrackQueue[0];
this._requestAddSendTrack(item);
} else if(retries <= 3) {
setTimeout(()=> {
this._nextTrack(retries++);
}, 3000)
}
} else if (this._direction == 'receive' && this._canProcessNextTrack) {
if(this._negotiationOfferUpdate.length) {
this._canProcessNextTrack = false;
this._sendTrackQueue = [];
this.handleProcessSDPOfferForRenegotiate();
} else if(this._addTrackQueue.length && !this._isReconnecting){
this._canProcessNextTrack = false;
this._sendTrackQueue = [];
while (this._addTrackQueue.length > 0) {
// if (this._addTrackQueue[0].mediaType != 2) {
let track = this._addTrackQueue.shift();
this._sendTrackQueue.push(track);
// } else {
// if (this._sendTrackQueue.length) {
// break;
// }
// this._sendTrackQueue.push(this._addTrackQueue.shift());
// break;
// }
}
this._requestReceiveTrack();
}
}
}
}
_requestAddSendTrack(item) {
let that = this,
localTrackIndex,
uuid = Utility.generateUUID();
item.uniqueId = uuid;
let sender = this._peer.peerConnection.getSenders().find(function (s, index) {
if (s.track == item.stream.getTracks()[0]) {
localTrackIndex = index;
return true;
}
});
if (sender) {
console.warn('Track already exists in connection, direction: send');
// item.onTrackCallback(item, item.stream.getTracks()[localTrackIndex])
}
let localStream;
if (item.topic.indexOf('Vi-') > -1 || item.topic.indexOf('screen') > -1) {
if (item.isScreenShare) {
localStream = this._app.call.deviceManager.mediaStreams.getScreenShareInput();
} else {
localStream = this._app.call.deviceManager.mediaStreams.getVideoInput();
}
if (localStream && !sender) {
this._peer.addTrack(localStream.getTracks()[0], localStream);
}
} else {
localStream = this._app.call.deviceManager.mediaStreams.getAudioInput();
if (localStream && !sender) {
this._peer.addTrack(localStream.getTracks()[0], localStream);
}
}
this._peer.peerConnection.onicecandidate = ({candidate}) => {
if(!candidate)
return;
this._app.call.currentCall().sendCallMessage({
id: "SEND_ADD_ICE_CANDIDATE",
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
iceCandidate: JSON.stringify(candidate),
}, null, {});
};
item.onTrackCallback(item, localStream.getTracks()[0])
if (this._firstSub) {
this._firstSub = false;
item.isFirstSub = true;
if(!item.offer) {
that._peer.peerConnection
.createOffer()
.then(offer => {
if(this._addTrackQueue[0])
this._addTrackQueue[0].offer = offer;
else
return;
return that._peer.peerConnection.setLocalDescription(offer)
})
.then(() => {
// console.log('[debug][_requestAddSendTrack] SEND_SDP_OFFER', this._peer.peerConnection.localDescription.sdp, [{
// mline: item.mline,
// topic: item.topic,
// mediaType: item.mediaType
// }]);
that._sendSdpOfferMessage(item, uuid);
})
.catch(error => console.error({error}));
} else {
that._sendSdpOfferMessage(item, uuid);
}
} else {
if(!item.offer) {
that._peer.peerConnection
.createOffer()
.then(offer => {
this._addTrackQueue[0].offer = offer;
return that._peer.peerConnection.setLocalDescription(offer)
})
.then(() => {
this._app.logger.log(
this._app.logger.tags.CALL_PROCESS,
"[_requestAddSendTrack][SEND_NEGOTIATION] ",
item
);
// console.log('[debug][_requestAddSendTrack] SEND_NEGOTIATION', this._peer.peerConnection.localDescription.sdp, [{
// mline: item.mline,
// topic: item.topic,
// mediaType: item.mediaType
// }]);
that._sendNegotioateMessage(item, uuid)
});
} else {
that._sendNegotioateMessage(item, uuid)
}
}
this.setSendRequestTimeout(uuid, item);
}
_requestRemoveSendTrackNoMessage(item){
if (this._isDestroyed)
return;
let localTrackIndex;
let sender = this._peer.peerConnection.getSenders().find(function (s, index) {
if (s.track == item.stream.getTracks()[0]) {
localTrackIndex = index;
return true;
}
});
if (sender) {
this._peer.peerConnection.removeTrack(sender);
}
}
_sendSdpOfferMessage(item, uuid) {
this._app.call.currentCall().sendCallMessage({
id: "SEND_SDP_OFFER",
sdpOffer: this._peer.peerConnection.localDescription.sdp,
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
addition: [{
mline: item.mline,
topic: item.topic,
mediaType: item.mediaType
}],
uniqueId: uuid,
}, null, {});
}
_sendNegotioateMessage(item, uuid) {
this._app.call.currentCall().sendCallMessage({
id: "SEND_NEGOTIATION",
sdpOffer: this._peer.peerConnection.localDescription.sdp,
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
addition: [{
mline: item.mline,
topic: item.topic,
mediaType: item.mediaType
}],
uniqueId: uuid,
}, null, {});
}
_requestRemoveSendTrack(item) {
if (this._isDestroyed)
return;
let localTrackIndex;
let sender = this._peer.peerConnection.getSenders().find(function (s, index) {
if (s.track == item.stream.getTracks()[0]) {
localTrackIndex = index;
return true;
}
});
if (sender) {
this._peer.peerConnection.removeTrack(sender);
// this._trackList.forEach((it, index) => {
// if (item.topic == it.topic) {
// delete this._trackList[index]
// }
// })
this._peer.peerConnection
.createOffer()
.then(offer => this._peer.peerConnection.setLocalDescription(offer))
.then(() => {
this._app.call.currentCall().sendCallMessage({
id: "SEND_NEGOTIATION",
sdpOffer: this._peer.peerConnection.localDescription.sdp,
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
deletion: [{
mline: item.mline,
topic: item.topic,
mediaType: item.mediaType
}]
}, null, {});
});
}
}
_requestReceiveTrack() {
let list = [];
let includedItem = [];
this._sendTrackQueue.forEach(item => {
if(!includedItem.includes(item.topic)) {
list.push({
clientId: item.clientId,
mline: item.mline,
topic: item.topic,
mediaType: item.mediaType
});
includedItem.push(item.topic);
}
});
let uuid = Utility.generateUUID();
this._sendRequestReceiveTrack(uuid, list);
// this._sendTrackQueue = this._sendTrackQueue.filter(it => {
// return !items.find(ite => ite.topic == it.topic);
// });
}
_sendRequestReceiveTrack(uuid, list) {
console.log('[media][SUBSCRIBE/UPDATE] Sending.. , clientIds: ',
list.map(it => it.clientId).join(','),
' ||| topics: ',
list.map(it => it.topic).join(','),
'||| _trackList: ',
JSON.stringify(this._trackList),
'||| _sendTrackQueue: ',
JSON.stringify(this._sendTrackQueue),
'||| _addTrackQueue: ',
JSON.stringify(this._addTrackQueue),
);
if (this._firstSub) {
this._app.call.currentCall().sendCallMessage({
id: 'SUBSCRIBE',
uniqueId: uuid,
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
addition: list
}, null, {});
} else {
this._app.call.currentCall().sendCallMessage({
id: 'UPDATE',
uniqueId: uuid,
token: this._app.sdkParams.token,
chatId: this._callId,
brokerAddress: this._brokerAddress,
addition: list
}, null, {});
}
this.setRequestTimeout(uuid, list);
}
setSendRequestTimeout(uuid, item) {
this._requestTimeouts[uuid] = {
timeout: setTimeout(() => {
// this.removeFailedTrack(item);
// item.onOpenFailure && item.onOpenFailure(item);
delete this._requestTimeouts[uuid];
this._onSendTrackFailed();
return;
if(item.isFirstSub) {
this._firstSub = true;
}
// this._requestRemoveSendTrackNoMessage(item);
// this.processingCurrentTrackCompleted();
this._unlockProcessingNextTrack();
this._nextTrack();
delete this._requestTimeouts[uuid];
}, 10000)
};
}
resetSubscribeOrUpdateFailedCount(){
this._subscribeOrUpdateFailedCount = 0;
}
setRequestTimeout(uuid, list) {
this._requestTimeouts[uuid] = {
timeout: setTimeout(() => {
if (!this._app.call.currentCall() || this._isDestroyed)
return;
if(this._subscribeOrUpdateFailedCount < 3) {
this._subscribeOrUpdateFailedCount += 1;
/**
* This will resend the request and waits for the response
*/
delete this._requestTimeouts[uuid];
this._sendRequestReceiveTrack(uuid, list);
// this._app.call.currentCall().sendCallMessage({
// id: 'REQUEST_RECEIVING_MEDIA',
// token: this._app.sdkParams.token,
// chatId: this._callId,
// brokerAddress: this._brokerAddress,
// }, null, {});
// this.processingCurrentTrackCompleted();
// delete this._requestTimeouts[uuid];
} else {
// items.forEach(it => {
// this.removeFailedTrack(it);
// it.onOpenFailure && it.onOpenFailure(it);
// });
this.resetSubscribeOrUpdateFailedCount();
if(this._app.call.currentCall()) {
// this._app.chatEvents.fireEvent('callEvents', {
// type: 'CALL_ENDED',
// callId: this._app.call.currentCall().getCallId()
// });
this._app.call.currentCall().raiseCallError(errorList.UPDATE_TRACK_REQUEST_TIMEOUT, null, true);
this._app.chatEvents.fireEvent('callEvents', {
type: 'YOU_DROPPED_FROM_CALL',
callId: this._app.call.currentCall().getCallId(),
result: {
callId: this._app.call.currentCall().getCallId(),
userId: this._app.store.user.get().id,
}
});
this._app.call.endCall({callId: this._app.call.currentCall().getCallId()})
}
}
}, 2000)
};
}
removeFailedTrack(item) {
this._addTrackQueue = this._addTrackQueue.filter(it => it.topic != item.topic);
this._sendTrackQueue = [];
this._trackList = this._trackList.filter(it => it.topic != item.topic);
// console.log('debug removed failed track', {item}, JSON.stringify(this._trackList), JSON.stringify(this._addTrackQueue));
}
requestReceiveError(uuid) {
if (this._requestTimeouts[uuid]) {
let item = this._trackList.find(item => {
return item && item.topic === this._requestTimeouts[uuid].topic;
});
this.removeRequestTimeout(uuid);
this.removeFailedTrack(item);
this.processingCurrentTrackCompleted();
item.onOpenFailure(item);
}
}
removeRequestTimeout(uuid) {
let record = this._requestTimeouts[uuid];
if (record) {
if (record.timeout)
clearTimeout(record.timeout);
delete this._requestTimeouts[uuid];
}
}
removeRequestTimeoutByTopic(topic){
this._trackList.forEach(item => {
if(item.topic == topic) {
if(this._requestTimeouts[item.uniqueId]) {
if (this._requestTimeouts[item.uniqueId].timeout) {
clearTimeout(this._requestTimeouts[item.uniqueId].timeout);
}
if (this._requestTimeouts[item.uniqueId])
delete this._requestTimeouts[item.uniqueId]
}
}
})
}
_unlockProcessingNextTrack() {
this._canProcessNextTrack = true;
}
_setPeerState(state) {
this._state = state;
}
addTrack(data) {
let existingTrack = this._trackList.some(item => item.topic == data.topic);
if (existingTrack)
return 2;
if (this._direction == 'send') {
data.mline = this._nextTrackMid;
this._nextTrackMid++;
}
this._trackList.push(data);
this._addTrackQueue.push(data);
if (this._direction == 'send') {
this._nextTrack();
} else {
/**
* Gives some time so other users have enough time to add their tracks.
* Otherwise, if receiving_media gives 5 tracks together they will open one by one
*/
this._addTrackQueueTriggerTimeout && clearTimeout(this._addTrackQueueTriggerTimeout);
this._addTrackQueueTriggerTimeout = setTimeout(()=> {
this._nextTrack();
}, 20);
}
return 1;
}
removeTrack(topic) {
let item = this._trackList.find(it => {
return it && it.topic === topic;
});
if (this._direction == 'send') {
if (item) {
this._requestRemoveSendTrack(item);
}
}
if (item) {
this._midManager.removeTopic(topic);
this._trackList = this._trackList.filter(it => it.topic != item.topic);
}
}
processingCurrentTrackCompleted() {
if(this._direction == 'send')
this._addTrackQueue.shift();
this._unlockProcessingNextTrack();
this._nextTrack();
}
maybeNextTrack() {
this._nextTrack();
}
isPeerConnecting() {
return this._state === this._peerStates.CONNECTING;
}
isPeerFailed() {
return this._state === this._peerStates.FAILED;
}
isPeerConnected() {
return this._state === this._peerStates.CONNECTED;
}
isPeerDisconnected() {
return this._state === this._peerStates.DISCONNECTED;
}
_onConnectionStateChange() {
if (!this._peer || this.isDestroyed()) {
return;
}
this._app.chatEvents.fireEvent("callStreamEvents", {
type: 'WEBRTC_CONNECTION_STATE_CHANGE',
callId: this._callId,
direction: this._direction,
connectionState: this._peer.peerConnection.connectionState,
});
this._app.logger.log(
this._app.logger.tags.CALL_PROCESS.id,
"[peerConnection.onconnectionstatechange]",
"peer: " + this._direction + " peerConnection.connectionState: " + this._peer.peerConnection.connectionState
);
// this._app.sdkParams.consoleLogging && console.log("[SDK][peerConnection.onconnectionstatechange] ", "peer: ", this._direction, " peerConnection.connectionState: ", this._peer.peerConnection.connectionState);
switch (this._peer.peerConnection.connectionState) {
case 'disconnected':
if (this.isPeerDisconnected())
return;
this._state = this._peerStates.DISCONNECTED;
this._app.call.currentCall().sendCallStatusEvent();
break;
case 'failed':
if (this.isPeerFailed())
return;
this._app.chatEvents.fireEvent('callEvents', {
type: 'PEER_FAILED',
callId: this._callId,
direction: this._direction
});
this._handlePeerFailed();
break
case 'connected':
if (this.isPeerConnected())
return;
this._nextTrack();
this._state = this._peerStates.CONNECTED;
this._app.call.currentCall().sendCallStatusEvent();
this._app.chatEvents.fireEvent('callEvents', {
type: 'PEER_CONNECTED',
callId: this._callId,
direction: this._direction
});
break;
}
}
_onIceConnectionStateChange() {
if (!this._peer || this.isDestroyed()) {
return; //avoid log errors
}
this._app.logger.log(
this._app.logger.tags.CALL_PROCESS.id,
"[peerConnection.oniceconnectionstatechange]",
"peer: " + this._direction + " peerConnection.connectionState: " + this._peer.peerConnection.connectionState
);
// this._app.sdkParams.consoleLogging && console.log("[SDK][oniceconnectionstatechange] ", "peer: ", this._direction, " peerConnection.connectionState: ", this._peer.peerConnection.iceConnectionState);
switch (this._peer.peerConnection.iceConnectionState) {
case 'disconnected':
if (this.isPeerDisconnected())
return;
this._state = this._peerStates.DISCONNECTED;
this._app.call.currentCall().sendCallStatusEvent();
// this._app.chatEvents.fireEvent('callEvents', {
// type: 'CALL_STATUS',
// callId: this._callId,
// errorCode: 7000,
// errorMessage: `Call Peer (${this._direction}) is disconnected!`,
// errorInfo: this._peer
// });
// this._app.sdkParams.consoleLogging && console.log('[SDK][oniceconnectionstatechange]:[disconnected] Internet connection failed, Reconnect your call, peer:', this._direction);
break;
case 'failed':
if (this.isPeerFailed())
return;
this._app.chatEvents.fireEvent('callEvents', {
type: 'PEER_FAILED',
callId: this._callId,
direction: this._direction
});
this._handlePeerFailed();
break
case 'connected':
if (this.isPeerConnected())
return;
this._state = this._peerStates.CONNECTED;
this._app.call.currentCall().sendCallStatusEvent();
this._app.chatEvents.fireEvent('callEvents', {
type: 'PEER_CONNECTED',
callId: this._callId,
direction: this._direction
});
// this._app.chatEvents.fireEvent('callEvents', {
// type: 'CALL_STATUS',
// callId: this._callId,
// errorCode: 7000,
// errorMessage: `Call Peer (${this._direction}) has connected!`,
// errorInfo: this._peer.peerConnection
// });
break;
}
}
_handlePeerFailed() {
this._state = this._peerStates.FAILED;
this._app.call.currentCall().sendCallStatusEvent();
// this._app.chatEvents.fireEvent('callEvents', {
// type: 'CALL_STATUS',
// callId: this._callId,
// errorCode: 7000,
// errorMessage: `Call Peer (${this._direction}) has failed!`,
// errorInfo: this._peer
// });
this._onPeerFailed(this._direction);
}
addIceCandidateToQueue(candidate) {
if (this._peer.peerConnection.signalingState === 'stable' && !!this._peer.peerConnection.currentRemoteDescription) {
this.addIceCandidate(candidate)
.catch(error => {
this._addIceQueue.push(candidate);
});
} else {
this._addIceQueue.push(candidate);
}
}
addIceOnPeer(event) {
let that = this;
that._addIceQueue.forEach(item => {
that.addIceCandidate(item);
});
}
watchConnectionStateChange() {
let that = this;
this._peer.peerConnection.onsignalingstatechange = (event) => {
if (that._peer.peerConnection.signalingState === 'stable' && !!that._peer.peerConnection.currentRemoteDescription) {
that.addIceOnPeer(event);
}
}
}
async addIceCandidate(data) {
return new Promise((resolve, reject) => {
this._peer.peerConnection
.addIceCandidate(data)
.catch(err => {
if (err) {
console.warn("[peerConnectionManager addIceCandidate" + this._direction + "] " + err);
reject(err);
}
});
});
}
reconnectPeer() {
this._destroyPeer();
this._peer = new WebrtcPeerConnection(this._defaultConfig);
}
async handleProcessSDPOfferForReceiveTrack(jsonMessage, callback) {
this._firstSub = false;
let topics = JSON.parse(jsonMessage.topic);
await this._midManager.reOpenExistingDuplicateMids(topics);
console.log('[media][PROCESS_SDP_OFFER/UPDATE] Received.. , clientIds: ', topics.map(it => it.clientId).join(','),' ||| topics: ', topics.map(it => it.topic).join(','), ', sendTrackQueue: ', this._sendTrackQueue);
this._sendTrackQueue.forEach(item => {
topics.forEach(it => {
if (item.topic == it.topic) {
item.onUpdateSuccess(item)
}
})
})
this._bindReceiveIceCandidateCallback();
let timeoutsList = {};
this._sendTrackQueue.forEach(item => {
topics.forEach(it => {
if (item.topic == it.topic) {
timeoutsList[it.topic] = setTimeout(() => {
let uId = this._app.call.currentCall().users().findUserIdByTopic(it.topic);
if(!uId)
return;
// this._app.call.currentCall().users().get(uId).trackOpenQueue.add();
if(it.mediaType == 0 || it.mediaType == 2) {
this._app.call.currentCall().users().get(uId).stopVideo();
setTimeout(()=>{
this._app.call.currentCall().users().get(uId).startVideo(it.topic.replace('Vi-', ''), it);
setTimeout(()=>{
this.maybeNextTrack();
}, 200)
}, 200)
} else if(it.mediaType == 0) {
this._app.call.currentCall().users().get(uId).stopAudio();
setTimeout(()=>{
this._app.call.currentCall().users().get(uId).startAudio(it.topic.replace('Vo-', ''), it);
setTimeout(()=>{
this.maybeNextTrack();
}, 200)
}, 200)
}
}, 5000)
}
})
});
this._peer.peerConnection.ontrack = (event) => {
const transceiver = event.transceiver;
console.log('[media][ontrack] ', {topics, transceiver});
if (transceiver.receiver.track) {
transceiver.receiver.track.onunmute = () => {
console.log('[media][track.onunmute] ', {topics, transceiver});
// if (topics.length == 1 && topics[0].mediaType == 2)
// screenShareTopicHandler(transceiver);
// else
normalTopicHandler(topics, transceiver);
transceiver.receiver.track.onunmute = null;
}
transceiver.receiver.track.onmute = () => {
console.log('[media][track.onmute] debug transceiver.receiver.track.onmute', {transceiver});
transceiver.receiver.track.onmute = null;
// this.repairMutedTracks();
}
}
/**
* Disable on track when we are done adding requested tracks
*/
this._onTrackCalls++;
if (this._onTrackCalls == topics.length) {
this._peer.peerConnection.ontrack = null;
this._onTrackCalls = 0;
}
}
let that = this;
function normalTopicHandler(topics2, transceiver) {
let cTrackData;
topics2.forEach(item => {
if (item.mids.includes(transceiver.mid)) {
that._trackList.forEach(it => {
if (it.topic === item.topic) {
clearTimeout(timeoutsList[it.topic]);
console.log('[media][onTrack][match] , clientId: ', it.clientId,' , topic: ', it.topic, ', mid: ', transceiver.mid, {transceiver});
it.mid = transceiver.mid;
it.transceiver = transceiver;
cTrackData = it;
}
});
}
});
if (cTrackData) {
cTrackData.track = transceiver.receiver.track;
cTrackData.onTrackCallback(cTrackData, transceiver.receiver.track);
} else {
console.error('[media][onTrack][mismatch] normalTopicHandler cTrackData not found.', {transceiver}, {topics});
}
}
function screenShareTopicHandler(transceiver) {
let cTrackData;
cTrackData = that._trackList.find(item => item.mediaType == 2);
if (cTrackData) {
clearTimeout(timeoutsList[cTrackData.topic]);
cTrackData.track = transceiver.receiver.track;
cTrackData.onTrackCallback(cTrackData, transceiver.receiver.track);
} else {
console.error('[SDK][ontrack] screenShareTopicHandler cTrackData not found.', {transceiver}, {topics});
}
}
this._peer.processOffer(jsonMessage.sdpOffer, (error, sdpAnswer) => {
if (error) {
console.error('[SDK][processOffer] Error: ', {error});
return;
}
console.log('[media][RECIVE_SDP_ANSWER] Sending.. , clientIds: ', topics.map(it => it.clientId).join(','),' ||| topics: ', topics.map(it => it.topic).join(','));
this._app.call.currentCall().sendCallMessage({
id: "RECIVE_SDP_ANSWER",
sdpAnswer,
token: this._app.sdkParams.token,
brokerAddress: this._brokerAddress,
addition: topics
}, null, {});
setTimeout(() => {
callback && callback();
});
});
}
handleProcessSDPOfferForReceiveTrackUseExsistingTracks(jsonMessage, callback) {
let that = this;
this._firstSub = false;
let topics = JSON.parse(jsonMessage.topic);
console.log('[media][PROCESS_SDP_OFFER/UPDATE using exist tracks] Received.. , clientIds: ', topics.map(it => it.clientId).join(','),' ||| topics: ', topics.map(it => it.topic).join(','), ', sendTrackQueue: ', this._sendTrackQueue);
this._sendTrackQueue.forEach(item => {
topics.forEach(it => {
if (item.topic == it.topic) {
it.onTrackCallback(it);
}
})
});
// this._bindReceiveIceCandidateCallback();
// this._peer.peerConnection.ontrack = (event) => {
// const transceiver = event.transceiver;
// console.log('[media][ontrack] ', {topics, transceiver});
// if (transceiver.receiver.track) {
// transceiver.receiver.track.onunmute = () => {
// console.log('[media][track.onunmute] ', {topics, transceiver});
//
// if (topics.length == 1 && topics[0].mediaType == 2)
// screenShareTopicHandler(transceiver);
// else
// normalTopicHandler(topics, transceiver);
//
// transceiver.receiver.track.onunmute = null;
// }
// transceiver.receiver.track.onmute = () => {
// console.log('[media][track.onmute] debug transceiver.receiver.track.onmute', {transceiver});
// transceiver.receiver.track.onmute = null;
// this.repairMutedTracks();
// }
//
// }
//
// /**
// * Disable on track when we are done adding requested tracks
// */
// this._onTrackCalls++;
// if (this._onTrackCalls == topics.length) {
// this._peer.peerConnection.ontrack = null;
// this._onTrackCalls = 0;
// }
// }
// function normalTopicHandler(topics2, transceiver) {
// let cTrackData;
// topics2.forEach(item => {
// if (item.mids.includes(transceiver.mid)) {
// that._trackList.forEach(it => {
// if (it.topic === item.topic) {
// console.log('[media][onTrack][match] , clientId: ', it.clientId,' , topic: ', it.topic, ', mid: ', transceiver.mid, {transceiver});
//
// console.log('debug matched mid: ', transceiver.mid, ' for topic: ', item.topic, transceiver);
// it.mid = transceiver.mid;
// cTrackData = it;
// }
// });
// }
// });
// if (cTrackData) {
// cTrackData.track = transceiver.receiver.track;
// cTrackData.onTrackCallback(cTrackData, transceiver.receiver.track);
// } else {
// console.error('[media][onTrack][mismatch] normalTopicHandler cTrackData not found.', {transceiver}, {topics});
//
// console.error('[SDK][ontrack] normalTopicHandler cTrackData not found.', {transceiver}, {topics});
// }
// }
// function screenShareTopicHandler(transceiver) {
// let cTrackData;
// cTrackData = that._trackList.find(item => item.mediaType == 2);
// if (cTrackData) {
// cTrackData.track = transceiver.receiver.track;
// cTrackData.onTrackCallback(cTrackData, transceiver.receiver.track);
// } else {
// console.error('[SDK][ontrack] screenShareTopicHandler cTrackData not found.', {transceiver}, {topics});
// }
// }
this._peer.processOffer(jsonMessage.sdpOffer, (error, sdpAnswer) => {
if (error) {
console.error('[SDK][processOffer] Error: ', {error});
return;
}
console.log('[media][RECIVE_SDP_ANSWER] Sending.. , clientIds: ', topics.map(it => it.clientId).join(','),' ||| topics: ', topics.map(it => it.topic).join(','));
this._app.call.currentCall().sendCallMessage({
id: "RECIVE_SDP_ANSWER",
sdpAnswer,
token: this._app.sdkParams.token,
brokerAddress: this._brokerAddress,
addition: topics
}, null, {});
setTimeout(() => {
callback && callback();
});
});
}
addNegotiationOfferToQueue(offer, topics) {
this._negotiationOfferUpdate.push({offer, topics})
// this._negotiationOfferUpdate.sdpOffer = offer;
// this._negotiationOfferUpdate.topics = topics;
}
_bindReceiveIceCandidateCallback(){
this._peer.peerConnection.onicecandidate = ({candidate}) => {
if(!candidate)
return;
this._app.call.currentCall().sendCallMessage({
id: "RECIVE_ADD_ICE_CANDIDATE",
brokerAddress: this._brokerAddress,
token: this._app.sdkParams.token,
chatId: this._callId,
iceCandidate: JSON.stringify(candidate),
}, null, {});
};
}
handleProcessSDPOfferForRenegotiate() {
this._bindReceiveIceCandidateCallback();
let localSdpObject = this._negotiationOfferUpdate.shift();
this._peer.processOffer(localSdpObject.offer, (error, sdpAnswer) => {
if (error) {
console.error('[SDK][processOffer] Error: ', {error});
this._onPeerFailed(this._direction);
return;
}
this._app.call.currentCall().sendCallMessage({
id: "RECIVE_SDP_ANSWER",
sdpAnswer,
token: this._app.sdkParams.token,
brokerAddress: this._brokerAddress,
deletion: (!Array.isArray(localSdpObject.topics)? JSON.parse(localSdpObject.topics) : localSdpObject.topics)
}, null, {});
// setTimeout(() => {
// callback && callback();
// });
});
}
handleProcessLatestSDPOffer(jsonMessage, callback) {
this._bindReceiveIceCandidateCallback();
this._peer.processOffer(jsonMessage.sdpOffer, (error, sdpAnswer) => {
if (error) {
return;
}
this._app.call.currentCall().sendCallMessage({
id: "RECIVE_SDP_ANSWER",
sdpAnswer,
token: this._app.sdkParams.token,
brokerAddress: this._brokerAddress,
}, null, {});
setTimeout(() => {
callback && callback();
}, 100);
});
}
repairMutedTracks(){
this._trackList.forEach(item => {
if(item.track && item.track.mute) {
console.log('[media] repairMutedTracks()', item.topic, {item});
this.removeFailedTrack(item);
this.processingCurrentTrackCompleted();
item.onOpenFailure(item);
}
})
}
getPeer() {
return this._peer;
}
_destroyPeer() {
if(this._peer) {
this._peer.dispose();
this._peer = null;
}
}
async destroy() {
this._isDestroyed = true;
this._destroyPeer();
}
isDestroyed() {
return this._isDestroyed;
}
changeVideoSendStream(stream) {
const currentTrack = this._trackList.find(it => it.mediaType === 0)
if (!currentTrack) {
console.error("webcam src not found")
}
const newTrackId = this._app.call.currentCall().sendPeerManager().getPeer().switchVideoStream(currentTrack.currentTrackId, stream)
if (newTrackId) {
currentTrack.currentTrackId = newTrackId
}
}
getTracksList() {
return this._trackList;
}
getIsReConnecting() {
return this._isReconnecting;
}
setIsReconnecting(val){
this._isReconnecting = val;
}
sendOpenDone() {
this._sendOpenDone = true;
}
isSendOpenDone(){
return this._sendOpenDone;
}
getPeerStatus() {
if(this.isPeerConnected())
return 'connected';
else if(this.isPeerFailed())
return 'failed';
else if(this.isPeerConnecting())
return 'connecting';
else if(this.isPeerDisconnected())
return 'disconnected';
}
resetSendQueue(){
this._addTrackQueue = [];
this._sendTrackQueue = [];
this._canProcessNextTrack = true;
this._negotiationOfferUpdate = [];
}
}
export default PeerConnectionManager