openvidu-browser: move Transceiver creation in createOffer/createAnswer

pull/577/head
Juan Navarro 2021-05-19 19:37:48 +02:00
parent 84a5683b56
commit 60b24fd412
2 changed files with 91 additions and 149 deletions

View File

@ -806,20 +806,6 @@ export class Stream {
this.initHarkEvents(); // Init hark events for the local stream this.initHarkEvents(); // Init hark events for the local stream
} }
const userMediaConstraints = {
audio: this.isSendAudio(),
video: this.isSendVideo()
};
const options: WebRtcPeerConfiguration = {
mediaStream: this.mediaStream,
mediaConstraints: userMediaConstraints,
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
onexception: (exceptionName: ExceptionEventName, message: string, data?: any) => { this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]) },
iceServers: this.getIceServersConf(),
simulcast: false
};
const successOfferCallback = (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);
@ -884,13 +870,25 @@ export class Stream {
}); });
}; };
const config: WebRtcPeerConfiguration = {
mediaConstraints: {
audio: this.hasAudio,
video: this.hasVideo,
},
simulcast: false,
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
onexception: (exceptionName: ExceptionEventName, message: string, data?: any) => { this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]) },
iceServers: this.getIceServersConf(),
mediaStream: this.mediaStream,
};
if (reconnect) { if (reconnect) {
this.disposeWebRtcPeer(); this.disposeWebRtcPeer();
} }
if (this.displayMyRemote()) { if (this.displayMyRemote()) {
this.webRtcPeer = new WebRtcPeerSendrecv(options); this.webRtcPeer = new WebRtcPeerSendrecv(config);
} else { } else {
this.webRtcPeer = new WebRtcPeerSendonly(options); this.webRtcPeer = new WebRtcPeerSendonly(config);
} }
this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId); this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId);
this.webRtcPeer.createOffer().then(sdpOffer => { this.webRtcPeer.createOffer().then(sdpOffer => {
@ -936,19 +934,7 @@ export class Stream {
completeWebRtcPeerReceive(sdpOffer: string, reconnect: boolean): Promise<void> { completeWebRtcPeerReceive(sdpOffer: string, reconnect: boolean): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const offerConstraints = { logger.debug("'Session.subscribe(Stream)' called");
audio: this.inboundStreamOpts.hasAudio,
video: this.inboundStreamOpts.hasVideo
};
logger.debug("'Session.subscribe(Stream)' called. Constraints of generate SDP offer",
offerConstraints);
const options = {
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
onexception: (exceptionName: ExceptionEventName, message: string, data?: any) => { this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]) },
mediaConstraints: offerConstraints,
iceServers: this.getIceServersConf(),
simulcast: false
};
const successAnswerCallback = (sdpAnswer) => { const successAnswerCallback = (sdpAnswer) => {
logger.debug('Sending SDP answer to subscribe to ' logger.debug('Sending SDP answer to subscribe to '
@ -968,7 +954,18 @@ export class Stream {
}); });
}; };
this.webRtcPeer = new WebRtcPeerRecvonly(options); const config: WebRtcPeerConfiguration = {
mediaConstraints: {
audio: this.hasAudio,
video: this.hasVideo,
},
simulcast: false,
onicecandidate: this.connection.sendIceCandidate.bind(this.connection),
onexception: (exceptionName: ExceptionEventName, message: string, data?: any) => { this.session.emitEvent('exception', [new ExceptionEvent(this.session, exceptionName, this, message, data)]) },
iceServers: this.getIceServersConf(),
};
this.webRtcPeer = new WebRtcPeerRecvonly(config);
this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId); this.webRtcPeer.addIceConnectionStateChangeListener(this.streamId);
this.webRtcPeer.processRemoteOffer(sdpOffer) this.webRtcPeer.processRemoteOffer(sdpOffer)
.then(() => { .then(() => {
@ -1227,4 +1224,4 @@ export class Stream {
(report.type === 'candidate-pair' && report.nominated && report.bytesSent > 0); (report.type === 'candidate-pair' && report.nominated && report.bytesSent > 0);
} }
} }

View File

@ -111,112 +111,49 @@ export class WebRtcPeer {
* Only if the negotiation was initiated by the this peer * Only if the negotiation was initiated by the this peer
*/ */
createOffer(): Promise<RTCSessionDescriptionInit> { createOffer(): Promise<RTCSessionDescriptionInit> {
return new Promise((resolve, reject) => { const hasAudio = this.configuration.mediaConstraints.audio;
const hasAudio = this.configuration.mediaConstraints.audio; const hasVideo = this.configuration.mediaConstraints.video;
const hasVideo = this.configuration.mediaConstraints.video;
let offerPromise: Promise<RTCSessionDescriptionInit>; let promise: Promise<RTCSessionDescriptionInit>;
// TODO: Delete this conditional when all supported browsers are // TODO: Delete this conditional when all supported browsers are
// modern enough to implement the Transceiver methods. // modern enough to implement the Transceiver methods.
if ("addTransceiver" in this.pc) { if ("addTransceiver" in this.pc) {
logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it"); logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it");
// At this point, all "send" audio/video tracks have been added if (this.configuration.mediaStream) {
// with pc.addTrack(), which in modern versions of libwebrtc for (const track of this.configuration.mediaStream.getTracks()) {
// will have created Transceivers with "sendrecv" direction. this.pc.addTransceiver(track, {
// Source: [addTrack/9.3](https://www.w3.org/TR/2020/CRD-webrtc-20201203/#dom-rtcpeerconnection-addtrack). direction: this.configuration.mode,
// streams: [this.configuration.mediaStream],
// Here we just need to enforce that those Transceivers have the sendEncodings: [],
// correct direction, either "sendrecv" or "sendonly". });
//
// Otherwise, if the tracks are "recv", no Transceiver should
// have been added yet.
const tcs = this.pc.getTransceivers();
if (tcs.length > 0) {
// Assert correct mode.
if (
this.configuration.mode !== "sendrecv" &&
this.configuration.mode !== "sendonly"
) {
throw new Error(
"BUG: Transceivers added, but direction is not send"
);
}
for (const tc of tcs) {
tc.direction = this.configuration.mode;
logger.debug(
`RTCRtpTransceiver direction: ${tc.direction}`
);
}
} else {
if (this.configuration.mode !== "recvonly") {
throw new Error(
"BUG: Transceivers missing, but direction is not recv"
);
}
if (hasAudio) {
this.pc.addTransceiver("audio", {
direction: this.configuration.mode,
});
}
if (hasVideo) {
this.pc.addTransceiver("video", {
direction: this.configuration.mode,
});
}
} }
offerPromise = this.pc.createOffer();
} else {
logger.debug("[generateOffer] Method pc.getTransceivers() NOT available; using LEGACY offerToReceive{Audio,Video}");
// DEPRECATED: LEGACY METHOD: Old WebRTC versions don't implement
// Transceivers, and instead depend on the deprecated
// "offerToReceiveAudio" and "offerToReceiveVideo".
const constraints: RTCOfferOptions = {
offerToReceiveAudio:
this.configuration.mode !== "sendonly" && hasAudio,
offerToReceiveVideo:
this.configuration.mode !== "sendonly" && hasVideo,
};
logger.debug(
"RTCPeerConnection constraints: " +
JSON.stringify(constraints)
);
// @ts-ignore: Compiler is too clever and thinks this branch
// will never execute.
offerPromise = this.pc.createOffer(constraints);
} }
offerPromise promise = this.pc.createOffer();
.then((offer) => { } else {
logger.debug("Created SDP offer"); logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}");
return this.pc.setLocalDescription(offer);
})
.then(() => {
const localDescription = this.pc.localDescription;
if (!!localDescription) { // DEPRECATED: LEGACY METHOD: Old WebRTC versions don't implement
logger.debug( // Transceivers, and instead depend on the deprecated
"Local description set:", // "offerToReceiveAudio" and "offerToReceiveVideo".
localDescription.sdp
); const options: RTCOfferOptions = {
resolve(localDescription.sdp); offerToReceiveAudio:
} else { this.configuration.mode !== "sendonly" && hasAudio,
reject("Local description is not defined"); offerToReceiveVideo:
} this.configuration.mode !== "sendonly" && hasVideo,
}) };
.catch((error) => reject(error));
}); logger.debug("RTCPeerConnection.createOffer() options:", JSON.stringify(options));
// @ts-ignore: Compiler is too clever and thinks this branch
// will never execute.
promise = this.pc.createOffer(options);
}
return promise;
} }
/** /**
@ -224,24 +161,32 @@ export class WebRtcPeer {
* Only if the negotiation was initiated by the other peer * Only if the negotiation was initiated by the other peer
*/ */
createAnswer(): Promise<RTCSessionDescriptionInit> { createAnswer(): Promise<RTCSessionDescriptionInit> {
return new Promise((resolve, reject) => { const hasAudio = this.configuration.mediaConstraints.audio;
let offerAudio, offerVideo = true; const hasVideo = this.configuration.mediaConstraints.video;
if (!!this.configuration.mediaConstraints) {
offerAudio = (typeof this.configuration.mediaConstraints.audio === 'boolean') ? let promise: Promise<RTCSessionDescriptionInit>;
this.configuration.mediaConstraints.audio : true;
offerVideo = (typeof this.configuration.mediaConstraints.video === 'boolean') ? // TODO: Delete this conditional when all supported browsers are
this.configuration.mediaConstraints.video : true; // modern enough to implement the Transceiver methods.
if ("addTransceiver" in this.pc) {
logger.debug("[createAnswer] Method RTCPeerConnection.addTransceiver() is available; using it");
if (hasAudio) {
this.pc.addTransceiver("audio", {
direction: this.configuration.mode,
});
} }
const constraints: RTCOfferOptions = { if (hasVideo) {
offerToReceiveAudio: offerAudio, this.pc.addTransceiver("video", {
offerToReceiveVideo: offerVideo direction: this.configuration.mode,
}; });
this.pc.createAnswer(constraints).then(sdpAnswer => { }
resolve(sdpAnswer); }
}).catch(error => {
reject(error); // else, there is nothing to do; the legacy createAnswer() options do
}); // not offer any control over what tracks are included in the answer.
});
return this.pc.createAnswer();
} }
/** /**
@ -363,12 +308,12 @@ export class WebRtcPeer {
switch (iceConnectionState) { switch (iceConnectionState) {
case 'disconnected': case 'disconnected':
// Possible network disconnection // Possible network disconnection
const msg1 = 'IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "disconnected". Possible network disconnection'; const msg1 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "disconnected". Possible network disconnection';
logger.warn(msg1); logger.warn(msg1);
this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1); this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1);
break; break;
case 'failed': case 'failed':
const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') to "failed"'; const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') to "failed"';
logger.error(msg2); logger.error(msg2);
this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_FAILED, msg2); this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_FAILED, msg2);
break; break;