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
|
||||
};
|
||||
|
||||
const successCallback = (sdpOfferParam) => {
|
||||
const successOfferCallback = (sdpOfferParam) => {
|
||||
logger.debug('Sending SDP offer to publish as '
|
||||
+ this.streamId, sdpOfferParam);
|
||||
|
||||
|
@ -859,7 +859,7 @@ export class Stream extends EventDispatcher {
|
|||
reject('Error on publishVideo: ' + JSON.stringify(error));
|
||||
}
|
||||
} else {
|
||||
this.webRtcPeer.processAnswer(response.sdpAnswer, false)
|
||||
this.webRtcPeer.processRemoteAnswer(response.sdpAnswer)
|
||||
.then(() => {
|
||||
this.streamId = response.id;
|
||||
this.creationTime = response.createdAt;
|
||||
|
@ -894,10 +894,15 @@ export class Stream extends EventDispatcher {
|
|||
this.webRtcPeer = new WebRtcPeerSendonly(options);
|
||||
}
|
||||
this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId);
|
||||
this.webRtcPeer.generateOffer().then(sdpOffer => {
|
||||
successCallback(sdpOffer);
|
||||
this.webRtcPeer.createOffer().then(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 => {
|
||||
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
|
||||
*/
|
||||
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) => {
|
||||
|
||||
const offerConstraints = {
|
||||
|
@ -921,50 +950,60 @@ export class Stream extends EventDispatcher {
|
|||
simulcast: false
|
||||
};
|
||||
|
||||
const successCallback = (sdpOfferParam) => {
|
||||
logger.debug('Sending SDP offer to subscribe to '
|
||||
+ this.streamId, sdpOfferParam);
|
||||
const successAnswerCallback = (sdpAnswer) => {
|
||||
logger.debug('Sending SDP answer to subscribe to '
|
||||
+ this.streamId, sdpAnswer);
|
||||
|
||||
const method = reconnect ? 'reconnectStream' : 'receiveVideoFrom';
|
||||
const params = { sdpOffer: sdpOfferParam };
|
||||
const params = { sdpAnswer };
|
||||
params[reconnect ? 'stream' : 'sender'] = this.streamId;
|
||||
|
||||
this.session.openvidu.sendRequest(method, params, (error, response) => {
|
||||
if (error) {
|
||||
reject(new Error('Error on recvVideoFrom: ' + JSON.stringify(error)));
|
||||
reject(new Error('Error on receiveVideFrom: ' + JSON.stringify(error)));
|
||||
} else {
|
||||
// Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary
|
||||
// to add a timeout before calling PeerConnection#setRemoteDescription during
|
||||
// some time (400 ms) from the moment first subscriber stream is received
|
||||
if (this.session.isFirstIonicIosSubscriber) {
|
||||
this.session.isFirstIonicIosSubscriber = false;
|
||||
setTimeout(() => {
|
||||
// After 400 ms Ionic iOS subscribers won't need to run
|
||||
// PeerConnection#setRemoteDescription after 250 ms timeout anymore
|
||||
this.session.countDownForIonicIosSubscribersActive = false;
|
||||
}, 400);
|
||||
}
|
||||
const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive;
|
||||
this.webRtcPeer.processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => {
|
||||
logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed"));
|
||||
this.remotePeerSuccessfullyEstablished();
|
||||
this.initWebRtcStats();
|
||||
resolve();
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
resolve();
|
||||
// // Ios Ionic. Limitation: some bug in iosrtc cordova plugin makes it necessary
|
||||
// // to add a timeout before calling PeerConnection#setRemoteDescription during
|
||||
// // some time (400 ms) from the moment first subscriber stream is received
|
||||
// if (this.session.isFirstIonicIosSubscriber) {
|
||||
// this.session.isFirstIonicIosSubscriber = false;
|
||||
// setTimeout(() => {
|
||||
// // After 400 ms Ionic iOS subscribers won't need to run
|
||||
// // PeerConnection#setRemoteDescription after 250 ms timeout anymore
|
||||
// this.session.countDownForIonicIosSubscribersActive = false;
|
||||
// }, 400);
|
||||
// }
|
||||
// const needsTimeoutOnProcessAnswer = this.session.countDownForIonicIosSubscribersActive;
|
||||
// this.webRtcPeer.processAnswer(response.sdpAnswer, needsTimeoutOnProcessAnswer).then(() => {
|
||||
// logger.info("'Subscriber' (" + this.streamId + ") successfully " + (reconnect ? "reconnected" : "subscribed"));
|
||||
// this.remotePeerSuccessfullyEstablished();
|
||||
// this.initWebRtcStats();
|
||||
// resolve();
|
||||
// }).catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.webRtcPeer = new WebRtcPeerRecvonly(options);
|
||||
this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId);
|
||||
this.webRtcPeer.generateOffer()
|
||||
.then(sdpOffer => {
|
||||
successCallback(sdpOffer);
|
||||
this.webRtcPeer.processRemoteOffer(sdpOffer)
|
||||
.then(() => {
|
||||
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 => {
|
||||
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
|
||||
* to send to OpenVidu Server (will be the remote description of other peer)
|
||||
* Creates an SDP offer from the local RTCPeerConnection to send to the other peer
|
||||
* Only if the negotiation was initiated by the this peer
|
||||
*/
|
||||
generateOffer(): Promise<string> {
|
||||
createOffer(): Promise<RTCSessionDescriptionInit> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let offerAudio, offerVideo = true;
|
||||
|
||||
|
@ -140,52 +140,32 @@ export class WebRtcPeer {
|
|||
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
|
||||
|
||||
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
|
||||
if (offerAudio) {
|
||||
this.pc.addTransceiver('audio', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
|
||||
if (offerVideo) {
|
||||
this.pc.addTransceiver('video', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
|
||||
this.pc
|
||||
.createOffer()
|
||||
this.pc.createOffer()
|
||||
.then(offer => {
|
||||
logger.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(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');
|
||||
}
|
||||
resolve(offer);
|
||||
})
|
||||
.catch(error => reject(error));
|
||||
|
||||
} else {
|
||||
|
||||
// Rest of platforms
|
||||
this.pc.createOffer(constraints).then(offer => {
|
||||
logger.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(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');
|
||||
}
|
||||
this.pc.createOffer(constraints)
|
||||
.then(offer => {
|
||||
logger.debug('Created SDP offer');
|
||||
resolve(offer);
|
||||
})
|
||||
.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
|
||||
* just needs to set the answer as its remote description
|
||||
* Creates an SDP answer from the local RTCPeerConnection to send to the other peer
|
||||
* 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) => {
|
||||
const answer: RTCSessionDescriptionInit = {
|
||||
type: 'answer',
|
||||
|
@ -205,34 +270,33 @@ export class WebRtcPeer {
|
|||
logger.debug('SDP answer received, setting remote description');
|
||||
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
reject('RTCPeerConnection is closed');
|
||||
reject('RTCPeerConnection is closed when trying to set remote description');
|
||||
}
|
||||
|
||||
this.setRemoteDescription(answer, needsTimeoutOnProcessAnswer, resolve, reject);
|
||||
|
||||
this.setRemoteDescription(answer, false)
|
||||
.then(() => resolve())
|
||||
.catch(error => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike<string> | undefined) => void, reject: (reason?: any) => void) {
|
||||
if (platform['isIonicIos']) {
|
||||
// Ionic iOS platform
|
||||
if (needsTimeoutOnProcessAnswer) {
|
||||
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
|
||||
setTimeout(() => {
|
||||
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
|
||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
||||
}, 250);
|
||||
} else {
|
||||
// 400 ms have elapsed
|
||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
||||
}
|
||||
} else {
|
||||
async setRemoteDescription(sdp: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean): Promise<void> {
|
||||
// if (platform['isIonicIos']) {
|
||||
// // Ionic iOS platform
|
||||
// if (needsTimeoutOnProcessAnswer) {
|
||||
// // 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
|
||||
// await new Promise(resolve => setTimeout(resolve, 250)); // Sleep for 250ms
|
||||
// logger.info('setRemoteDescription run after timeout for Ionic iOS device');
|
||||
// return this.pc.setRemoteDescription(sdp);
|
||||
// } else {
|
||||
// // 400 ms have elapsed
|
||||
// return this.pc.setRemoteDescription(sdp);
|
||||
// }
|
||||
// } else {
|
||||
// Rest of platforms
|
||||
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
|
||||
}
|
||||
return this.pc.setRemoteDescription(sdp);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue