mirror of https://github.com/OpenVidu/openvidu.git
openvidu-browser: mediasoup refactoring
parent
6f976246b4
commit
0d3d54351e
|
@ -822,7 +822,7 @@ export class Stream extends EventDispatcher {
|
||||||
simulcast: false
|
simulcast: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const successCallback = (sdpOfferParam) => {
|
const successOfferCallback = (sdpOfferParam) => {
|
||||||
logger.debug('Sending SDP offer to publish as '
|
logger.debug('Sending SDP offer to publish as '
|
||||||
+ this.streamId, sdpOfferParam);
|
+ this.streamId, sdpOfferParam);
|
||||||
|
|
||||||
|
@ -859,7 +859,7 @@ export class Stream extends EventDispatcher {
|
||||||
reject('Error on publishVideo: ' + JSON.stringify(error));
|
reject('Error on publishVideo: ' + JSON.stringify(error));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.webRtcPeer.processAnswer(response.sdpAnswer, false)
|
this.webRtcPeer.processRemoteAnswer(response.sdpAnswer)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.streamId = response.id;
|
this.streamId = response.id;
|
||||||
this.creationTime = response.createdAt;
|
this.creationTime = response.createdAt;
|
||||||
|
@ -894,10 +894,15 @@ export class Stream extends EventDispatcher {
|
||||||
this.webRtcPeer = new WebRtcPeerSendonly(options);
|
this.webRtcPeer = new WebRtcPeerSendonly(options);
|
||||||
}
|
}
|
||||||
this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId);
|
this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId);
|
||||||
this.webRtcPeer.generateOffer().then(sdpOffer => {
|
this.webRtcPeer.createOffer().then(sdpOffer => {
|
||||||
successCallback(sdpOffer);
|
this.webRtcPeer.processLocalOffer(sdpOffer)
|
||||||
|
.then(() => {
|
||||||
|
successOfferCallback(sdpOffer.sdp);
|
||||||
|
}).catch(error => {
|
||||||
|
reject(new Error('(publish) SDP process local offer error: ' + JSON.stringify(error)));
|
||||||
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(new Error('(publish) SDP offer error: ' + JSON.stringify(error)));
|
reject(new Error('(publish) SDP create offer error: ' + JSON.stringify(error)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -906,6 +911,30 @@ export class Stream extends EventDispatcher {
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
initWebRtcPeerReceive(reconnect: boolean): Promise<any> {
|
initWebRtcPeerReceive(reconnect: boolean): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.session.openvidu.sendRequest('prepareReceiveVideFrom', { sender: this.streamId }, (error, response) => {
|
||||||
|
if (error) {
|
||||||
|
reject(new Error('Error on prepareReceiveVideFrom: ' + JSON.stringify(error)));
|
||||||
|
} else {
|
||||||
|
this.completeWebRtcPeerReceive(response.sdpOffer, reconnect)
|
||||||
|
.then(() => {
|
||||||
|
logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed"));
|
||||||
|
this.remotePeerSuccessfullyEstablished();
|
||||||
|
this.initWebRtcStats();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
completeWebRtcPeerReceive(sdpOffer: string, reconnect: boolean): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const offerConstraints = {
|
const offerConstraints = {
|
||||||
|
@ -921,50 +950,60 @@ export class Stream extends EventDispatcher {
|
||||||
simulcast: false
|
simulcast: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const successCallback = (sdpOfferParam) => {
|
const successAnswerCallback = (sdpAnswer) => {
|
||||||
logger.debug('Sending SDP offer to subscribe to '
|
logger.debug('Sending SDP answer to subscribe to '
|
||||||
+ this.streamId, sdpOfferParam);
|
+ this.streamId, sdpAnswer);
|
||||||
|
|
||||||
const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom';
|
const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom';
|
||||||
const params = { sdpOffer: sdpOfferParam };
|
const params = { sdpAnswer };
|
||||||
params[reconnect ? 'stream' : 'sender'] = this.streamId;
|
params[reconnect ? 'stream' : 'sender'] = this.streamId;
|
||||||
|
|
||||||
this.session.openvidu.sendRequest(method, params, (error, response) => {
|
this.session.openvidu.sendRequest(method, params, (error, response) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(new Error('Error on recvVideoFrom: ' + JSON.stringify(error)));
|
reject(new Error('Error on receiveVideFrom: ' + JSON.stringify(error)));
|
||||||
} else {
|
} else {
|
||||||
// Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary
|
resolve();
|
||||||
// to add a timeout before calling PeerConnection#setRemoteDescription during
|
// // Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary
|
||||||
// some time (400 ms) from the moment first subscriber stream is received
|
// // to add a timeout before calling PeerConnection#setRemoteDescription during
|
||||||
if (this.session.isFirstIonicIosSubscriber) {
|
// // some time (400 ms) from the moment first subscriber stream is received
|
||||||
this.session.isFirstIonicIosSubscriber = false;
|
// if (this.session.isFirstIonicIosSubscriber) {
|
||||||
setTimeout(() => {
|
// this.session.isFirstIonicIosSubscriber = false;
|
||||||
// After 400 ms Ionic iOS subscribers won't need to run
|
// setTimeout(() => {
|
||||||
// PeerConnection#setRemoteDescription after 250 ms timeout anymore
|
// // After 400 ms Ionic iOS subscribers won't need to run
|
||||||
this.session.countDownForIonicIosSubscribersActive = false;
|
// // PeerConnection#setRemoteDescription after 250 ms timeout anymore
|
||||||
}, 400);
|
// this.session.countDownForIonicIosSubscribersActive = false;
|
||||||
}
|
// }, 400);
|
||||||
const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive;
|
// }
|
||||||
this.webRtcPeer.processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => {
|
// const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive;
|
||||||
logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed"));
|
// this.webRtcPeer.processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => {
|
||||||
this.remotePeerSuccessfullyEstablished();
|
// logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed"));
|
||||||
this.initWebRtcStats();
|
// this.remotePeerSuccessfullyEstablished();
|
||||||
resolve();
|
// this.initWebRtcStats();
|
||||||
}).catch(error => {
|
// resolve();
|
||||||
reject(error);
|
// }).catch(error => {
|
||||||
});
|
// reject(error);
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.webRtcPeer = new WebRtcPeerRecvonly(options);
|
this.webRtcPeer = new WebRtcPeerRecvonly(options);
|
||||||
this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId);
|
this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId);
|
||||||
this.webRtcPeer.generateOffer()
|
this.webRtcPeer.processRemoteOffer(sdpOffer)
|
||||||
.then(sdpOffer => {
|
.then(() => {
|
||||||
successCallback(sdpOffer);
|
this.webRtcPeer.createAnswer().then(sdpAnswer => {
|
||||||
|
this.webRtcPeer.processLocalAnswer(sdpAnswer)
|
||||||
|
.then(() => {
|
||||||
|
successAnswerCallback(sdpAnswer.sdp);
|
||||||
|
}).catch(error => {
|
||||||
|
reject(new Error('(subscribe) SDP process local answer error: ' + JSON.stringify(error)));
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
reject(new Error('(subscribe) SDP create answer error: ' + JSON.stringify(error)));
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
reject(new Error('(subscribe) SDP offer error: ' + JSON.stringify(error)));
|
reject(new Error('(subscribe) SDP process remote offer error: ' + JSON.stringify(error)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,10 +117,10 @@ export class WebRtcPeer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that creates an offer, sets it as local description and returns the offer param
|
* Creates an SDP offer from the local RTCPeerConnection to send to the other peer
|
||||||
* to send to OpenVidu Server (will be the remote description of other peer)
|
* Only if the negotiation was initiated by the this peer
|
||||||
*/
|
*/
|
||||||
generateOffer(): Promise<string> {
|
createOffer(): Promise<RTCSessionDescriptionInit> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let offerAudio, offerVideo = true;
|
let offerAudio, offerVideo = true;
|
||||||
|
|
||||||
|
@ -140,52 +140,32 @@ export class WebRtcPeer {
|
||||||
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
|
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
|
||||||
|
|
||||||
if (platform.name === 'Safari' && platform.ua!!.indexOf('Safari') !== -1) {
|
if (platform.name === 'Safari' && platform.ua!!.indexOf('Safari') !== -1) {
|
||||||
|
|
||||||
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
|
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
|
||||||
if (offerAudio) {
|
if (offerAudio) {
|
||||||
this.pc.addTransceiver('audio', {
|
this.pc.addTransceiver('audio', {
|
||||||
direction: this.configuration.mode,
|
direction: this.configuration.mode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offerVideo) {
|
if (offerVideo) {
|
||||||
this.pc.addTransceiver('video', {
|
this.pc.addTransceiver('video', {
|
||||||
direction: this.configuration.mode,
|
direction: this.configuration.mode,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.pc.createOffer()
|
||||||
this.pc
|
|
||||||
.createOffer()
|
|
||||||
.then(offer => {
|
.then(offer => {
|
||||||
logger.debug('Created SDP offer');
|
logger.debug('Created SDP offer');
|
||||||
return this.pc.setLocalDescription(offer);
|
resolve(offer);
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
const localDescription = this.pc.localDescription;
|
|
||||||
|
|
||||||
if (!!localDescription) {
|
|
||||||
logger.debug('Local description set', localDescription.sdp);
|
|
||||||
resolve(localDescription.sdp);
|
|
||||||
} else {
|
|
||||||
reject('Local description is not defined');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Rest of platforms
|
// Rest of platforms
|
||||||
this.pc.createOffer(constraints).then(offer => {
|
this.pc.createOffer(constraints)
|
||||||
logger.debug('Created SDP offer');
|
.then(offer => {
|
||||||
return this.pc.setLocalDescription(offer);
|
logger.debug('Created SDP offer');
|
||||||
})
|
resolve(offer);
|
||||||
.then(() => {
|
|
||||||
const localDescription = this.pc.localDescription;
|
|
||||||
if (!!localDescription) {
|
|
||||||
logger.debug('Local description set', localDescription.sdp);
|
|
||||||
resolve(localDescription.sdp);
|
|
||||||
} else {
|
|
||||||
reject('Local description is not defined');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
}
|
}
|
||||||
|
@ -193,10 +173,95 @@ export class WebRtcPeer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function invoked when a SDP answer is received. Final step in SDP negotiation, the peer
|
* Creates an SDP answer from the local RTCPeerConnection to send to the other peer
|
||||||
* just needs to set the answer as its remote description
|
* Only if the negotiation was initiated by the other peer
|
||||||
*/
|
*/
|
||||||
processAnswer(sdpAnswer: string, needsTimeoutOnProcessAnswer: boolean): Promise<string> {
|
createAnswer(): Promise<RTCSessionDescriptionInit> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let offerAudio, offerVideo = true;
|
||||||
|
if (!!this.configuration.mediaConstraints) {
|
||||||
|
offerAudio = (typeof this.configuration.mediaConstraints.audio === 'boolean') ?
|
||||||
|
this.configuration.mediaConstraints.audio : true;
|
||||||
|
offerVideo = (typeof this.configuration.mediaConstraints.video === 'boolean') ?
|
||||||
|
this.configuration.mediaConstraints.video : true;
|
||||||
|
}
|
||||||
|
const constraints: RTCOfferOptions = {
|
||||||
|
offerToReceiveAudio: offerAudio,
|
||||||
|
offerToReceiveVideo: offerVideo
|
||||||
|
};
|
||||||
|
this.pc.createAnswer(constraints).then(sdpAnswer => {
|
||||||
|
resolve(sdpAnswer);
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This peer initiated negotiation. Step 1/4 of SDP offer-answer protocol
|
||||||
|
*/
|
||||||
|
processLocalOffer(offer: RTCSessionDescriptionInit): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.pc.setLocalDescription(offer)
|
||||||
|
.then(() => {
|
||||||
|
const localDescription = this.pc.localDescription;
|
||||||
|
if (!!localDescription) {
|
||||||
|
logger.debug('Local description set', localDescription.sdp);
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject('Local description is not defined');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other peer initiated negotiation. Step 2/4 of SDP offer-answer protocol
|
||||||
|
*/
|
||||||
|
processRemoteOffer(sdpOffer: string): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const offer: RTCSessionDescriptionInit = {
|
||||||
|
type: 'offer',
|
||||||
|
sdp: sdpOffer
|
||||||
|
};
|
||||||
|
logger.debug('SDP offer received, setting remote description', offer);
|
||||||
|
|
||||||
|
if (this.pc.signalingState === 'closed') {
|
||||||
|
reject('RTCPeerConnection is closed when trying to set remote description');
|
||||||
|
}
|
||||||
|
// TODO: check if Ionic iOS still needs timeout on setting first remote description when subscribing
|
||||||
|
this.setRemoteDescription(offer, false)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Other peer initiated negotiation. Step 3/4 of SDP offer-answer protocol
|
||||||
|
*/
|
||||||
|
processLocalAnswer(answer: RTCSessionDescriptionInit): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
logger.debug('SDP answer created, setting local description');
|
||||||
|
if (this.pc.signalingState === 'closed') {
|
||||||
|
reject('RTCPeerConnection is closed when trying to set local description');
|
||||||
|
}
|
||||||
|
this.pc.setLocalDescription(answer)
|
||||||
|
.then(() => resolve())
|
||||||
|
.catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This peer initiated negotiation. Step 4/4 of SDP offer-answer protocol
|
||||||
|
*/
|
||||||
|
processRemoteAnswer(sdpAnswer: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const answer: RTCSessionDescriptionInit = {
|
const answer: RTCSessionDescriptionInit = {
|
||||||
type: 'answer',
|
type: 'answer',
|
||||||
|
@ -205,34 +270,33 @@ export class WebRtcPeer {
|
||||||
logger.debug('SDP answer received, setting remote description');
|
logger.debug('SDP answer received, setting remote description');
|
||||||
|
|
||||||
if (this.pc.signalingState === 'closed') {
|
if (this.pc.signalingState === 'closed') {
|
||||||
reject('RTCPeerConnection is closed');
|
reject('RTCPeerConnection is closed when trying to set remote description');
|
||||||
}
|
}
|
||||||
|
this.setRemoteDescription(answer, false)
|
||||||
this.setRemoteDescription(answer, needsTimeoutOnProcessAnswer, resolve, reject);
|
.then(() => resolve())
|
||||||
|
.catch(error => reject(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike<string> | undefined) => void, reject: (reason?: any) => void) {
|
async setRemoteDescription(sdp: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean): Promise<void> {
|
||||||
if (platform['isIonicIos']) {
|
// if (platform['isIonicIos']) {
|
||||||
// Ionic iOS platform
|
// // Ionic iOS platform
|
||||||
if (needsTimeoutOnProcessAnswer) {
|
// if (needsTimeoutOnProcessAnswer) {
|
||||||
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
|
// // 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
|
||||||
setTimeout(() => {
|
// await new Promise(resolve => setTimeout(resolve, 250)); // Sleep for 250ms
|
||||||
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
|
// logger.info('setRemoteDescription run after timeout for Ionic iOS device');
|
||||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
// return this.pc.setRemoteDescription(sdp);
|
||||||
}, 250);
|
// } else {
|
||||||
} else {
|
// // 400 ms have elapsed
|
||||||
// 400 ms have elapsed
|
// return this.pc.setRemoteDescription(sdp);
|
||||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
// }
|
||||||
}
|
// } else {
|
||||||
} else {
|
|
||||||
// Rest of platforms
|
// Rest of platforms
|
||||||
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
|
return this.pc.setRemoteDescription(sdp);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue