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
}
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) => {
logger.debug('Sending SDP offer to publish as '
+ 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) {
this.disposeWebRtcPeer();
}
if (this.displayMyRemote()) {
this.webRtcPeer = new WebRtcPeerSendrecv(options);
this.webRtcPeer = new WebRtcPeerSendrecv(config);
} else {
this.webRtcPeer = new WebRtcPeerSendonly(options);
this.webRtcPeer = new WebRtcPeerSendonly(config);
}
this.webRtcPeer.addIceConnectionStateChangeListener('publisher of ' + this.connection.connectionId);
this.webRtcPeer.createOffer().then(sdpOffer => {
@ -936,19 +934,7 @@ export class Stream {
completeWebRtcPeerReceive(sdpOffer: string, reconnect: boolean): Promise<void> {
return new Promise((resolve, reject) => {
const offerConstraints = {
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
};
logger.debug("'Session.subscribe(Stream)' called");
const successAnswerCallback = (sdpAnswer) => {
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.processRemoteOffer(sdpOffer)
.then(() => {
@ -1227,4 +1224,4 @@ export class Stream {
(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
*/
createOffer(): Promise<RTCSessionDescriptionInit> {
return new Promise((resolve, reject) => {
const hasAudio = this.configuration.mediaConstraints.audio;
const hasVideo = this.configuration.mediaConstraints.video;
const hasAudio = this.configuration.mediaConstraints.audio;
const hasVideo = this.configuration.mediaConstraints.video;
let offerPromise: Promise<RTCSessionDescriptionInit>;
let promise: Promise<RTCSessionDescriptionInit>;
// TODO: Delete this conditional when all supported browsers are
// modern enough to implement the Transceiver methods.
if ("addTransceiver" in this.pc) {
logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it");
// TODO: Delete this conditional when all supported browsers are
// modern enough to implement the Transceiver methods.
if ("addTransceiver" in this.pc) {
logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it");
// At this point, all "send" audio/video tracks have been added
// with pc.addTrack(), which in modern versions of libwebrtc
// will have created Transceivers with "sendrecv" direction.
// Source: [addTrack/9.3](https://www.w3.org/TR/2020/CRD-webrtc-20201203/#dom-rtcpeerconnection-addtrack).
//
// Here we just need to enforce that those Transceivers have the
// 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,
});
}
if (this.configuration.mediaStream) {
for (const track of this.configuration.mediaStream.getTracks()) {
this.pc.addTransceiver(track, {
direction: this.configuration.mode,
streams: [this.configuration.mediaStream],
sendEncodings: [],
});
}
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
.then((offer) => {
logger.debug("Created SDP offer");
return this.pc.setLocalDescription(offer);
})
.then(() => {
const localDescription = this.pc.localDescription;
promise = this.pc.createOffer();
} else {
logger.debug("[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}");
if (!!localDescription) {
logger.debug(
"Local description set:",
localDescription.sdp
);
resolve(localDescription.sdp);
} else {
reject("Local description is not defined");
}
})
.catch((error) => reject(error));
});
// DEPRECATED: LEGACY METHOD: Old WebRTC versions don't implement
// Transceivers, and instead depend on the deprecated
// "offerToReceiveAudio" and "offerToReceiveVideo".
const options: RTCOfferOptions = {
offerToReceiveAudio:
this.configuration.mode !== "sendonly" && hasAudio,
offerToReceiveVideo:
this.configuration.mode !== "sendonly" && hasVideo,
};
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
*/
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 hasAudio = this.configuration.mediaConstraints.audio;
const hasVideo = this.configuration.mediaConstraints.video;
let promise: Promise<RTCSessionDescriptionInit>;
// TODO: Delete this conditional when all supported browsers are
// 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 = {
offerToReceiveAudio: offerAudio,
offerToReceiveVideo: offerVideo
};
this.pc.createAnswer(constraints).then(sdpAnswer => {
resolve(sdpAnswer);
}).catch(error => {
reject(error);
});
});
if (hasVideo) {
this.pc.addTransceiver("video", {
direction: this.configuration.mode,
});
}
}
// 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) {
case 'disconnected':
// 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);
this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1);
break;
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);
this.configuration.onexception(ExceptionEventName.ICE_CONNECTION_FAILED, msg2);
break;