async createOffer()

in openmeetings-web/src/main/front/src/settings/WebRtcPeer.js [148:306]


	async createOffer() {
		// TODO: Delete this conditional when all supported browsers are
		// modern enough to implement the Transceiver methods.
		if (!('addTransceiver' in this.pc)) {
			OmUtil.error(
				'[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}'
			);
			return this.createOfferLegacy();
		} else {
			OmUtil.log('[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it');
		}

		// Spec doc: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver

		if (this.configuration.mode !== 'recvonly') {
			// To send media, assume that all desired media tracks have been
			// already added by higher level code to our MediaStream.

			if (!this.configuration.mediaStream) {
				throw new Error(
					`[WebRtcPeer.createOffer] Direction is '${this.configuration.mode}', but no stream was configured to be sent`
				);
			}

			for (const track of this.configuration.mediaStream.getTracks()) {
				const tcInit = {
					direction: this.configuration.mode,
					streams: [this.configuration.mediaStream]
				};

				if (track.kind === 'video' && this.configuration.simulcast) {
					// Check if the requested size is enough to ask for 3 layers.
					const trackSettings = track.getSettings();
					const trackConsts = track.getConstraints();

					const trackWidth = typeof(trackSettings.width) === 'object' ? trackConsts.width.ideal : trackConsts.width || 0;
					const trackHeight = typeof(trackSettings.height) === 'object' ? trackConsts.height.ideal : trackConsts.height || 0;
					OmUtil.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`);

					const trackPixels = trackWidth * trackHeight;
					let maxLayers = 0;
					if (trackPixels >= 960 * 540) {
						maxLayers = 3;
					} else if (trackPixels >= 480 * 270) {
						maxLayers = 2;
					} else {
						maxLayers = 1;
					}

					tcInit.sendEncodings = [];
					for (let l = 0; l < maxLayers; l++) {
						const layerDiv = 2 ** (maxLayers - l - 1);

						const encoding = {
							rid: 'rdiv' + layerDiv.toString(),

							// @ts-ignore -- Property missing from DOM types.
							scalabilityMode: 'L1T1'
						};

						if (['detail', 'text'].includes(track.contentHint)) {
							// Prioritize best resolution, for maximum picture detail.
							encoding.scaleResolutionDownBy = 1.0;

							// @ts-ignore -- Property missing from DOM types.
							encoding.maxFramerate = Math.floor(30 / layerDiv);
						} else {
							encoding.scaleResolutionDownBy = layerDiv;
						}

						tcInit.sendEncodings.push(encoding);
					}
				}

				const tc = this.pc.addTransceiver(track, tcInit);

				if (track.kind === 'video') {
					let sendParams = tc.sender.getParameters();
					let needSetParams = false;

					if (sendParams.degradationPreference && !sendParams.degradationPreference.length) {
						// degradationPreference for video: "balanced", "maintain-framerate", "maintain-resolution".
						// https://www.w3.org/TR/2018/CR-webrtc-20180927/#dom-rtcdegradationpreference
						if (['detail', 'text'].includes(track.contentHint)) {
							sendParams.degradationPreference = 'maintain-resolution';
						} else {
							sendParams.degradationPreference = 'balanced';
						}

						OmUtil.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`);

						// Firefox implements degradationPreference on each individual encoding!
						// (set it on every element of the sendParams.encodings array)

						needSetParams = true;
					}

					// Check that the simulcast encodings were applied.
					// Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings`
					// so the only way to enable simulcast is with `RTCRtpSender.setParameters()`.
					//
					// This next block can be deleted when Firefox fixes bug #1396918:
					// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
					//
					// NOTE: This is done in a way that is compatible with all browsers, to save on
					// browser-conditional code. The idea comes from WebRTC Adapter.js:
					// * https://github.com/webrtcHacks/adapter/issues/998
					// * https://github.com/webrtcHacks/adapter/blob/v7.7.0/src/js/firefox/firefox_shim.js#L231-L255
					if (this.configuration.simulcast) {
						if (sendParams.encodings.length !== tcInit.sendEncodings.length) {
							sendParams.encodings = tcInit.sendEncodings;

							needSetParams = true;
						}
					}

					if (needSetParams) {
						OmUtil.log(`[createOffer] Setting new RTCRtpSendParameters to video sender`);
						try {
							await tc.sender.setParameters(sendParams);
						} catch (error) {
							let message = `[WebRtcPeer.createOffer] Cannot set RTCRtpSendParameters to video sender`;
							if (error instanceof Error) {
								message += `: ${error.message}`;
							}
							throw new Error(message);
						}
					}
				}
			}
		} else {
			// To just receive media, create new recvonly transceivers.
			for (const kind of ['audio', 'video']) {
				// Check if the media kind should be used.
				if (!this.configuration.mediaConstraints[kind]) {
					continue;
				}

				this.configuration.mediaStream = new MediaStream();
				this.pc.addTransceiver(kind, {
					direction: this.configuration.mode,
					streams: [this.configuration.mediaStream]
				});
			}
		}

		let sdpOffer;
		try {
			sdpOffer = await this.pc.createOffer();
		} catch (error) {
			let message = `[WebRtcPeer.createOffer] Browser failed creating an SDP Offer`;
			if (error instanceof Error) {
				message += `: ${error.message}`;
			}
			throw new Error(message);
		}

		return sdpOffer;
	}