diff --git a/openvidu-browser/src/OpenVidu/Publisher.ts b/openvidu-browser/src/OpenVidu/Publisher.ts index d535371b..960c7807 100644 --- a/openvidu-browser/src/OpenVidu/Publisher.ts +++ b/openvidu-browser/src/OpenVidu/Publisher.ts @@ -27,6 +27,7 @@ import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/Open import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode'; import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger'; import { PlatformUtils } from '../OpenViduInternal/Utils/Platform'; +import { TypeOfVideo } from '../OpenViduInternal/Enums/TypeOfVideo'; /** * @hidden @@ -398,6 +399,37 @@ export class Publisher extends StreamManager { mediaStream.getVideoTracks()[0].enabled = enabled; } + // Set Content Hint on all MediaStreamTracks + for (const track of mediaStream.getAudioTracks()) { + if (!track.contentHint?.length) { + // contentHint for audio: "", "speech", "speech-recognition", "music". + // https://w3c.github.io/mst-content-hint/#audio-content-hints + track.contentHint = ""; + logger.info(`Audio track Content Hint set: '${track.contentHint}'`); + } + } + for (const track of mediaStream.getVideoTracks()) { + if (!track.contentHint?.length) { + // contentHint for video: "", "motion", "detail", "text". + // https://w3c.github.io/mst-content-hint/#video-content-hints + switch (this.stream.typeOfVideo) { + case TypeOfVideo.SCREEN: + track.contentHint = "detail"; + break; + case TypeOfVideo.CUSTOM: + logger.warn("CUSTOM type video track was provided without Content Hint!"); + track.contentHint = "motion"; + break; + case TypeOfVideo.CAMERA: + case TypeOfVideo.IPCAM: + default: + track.contentHint = "motion"; + break; + } + logger.info(`Video track Content Hint set: '${track.contentHint}'`); + } + } + this.initializeVideoReference(mediaStream); if (!this.stream.displayMyRemote()) { @@ -443,6 +475,7 @@ export class Publisher extends StreamManager { this.stream.isLocalStreamReadyToPublish = true; this.stream.ee.emitEvent('stream-ready-to-publish', []); } + return resolve(); }; @@ -606,7 +639,7 @@ export class Publisher extends StreamManager { /** * @hidden - * + * * To obtain the videoDimensions we wait for the video reference to have enough metadata * and then try to use MediaStreamTrack.getSettingsMethod(). If not available, then we * use the HTMLVideoElement properties videoWidth and videoHeight diff --git a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts index bfa3869a..60982d50 100644 --- a/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts +++ b/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts @@ -175,76 +175,55 @@ export class WebRtcPeer { streams: [this.configuration.mediaStream], }; - if (track.kind === "audio") { - if ("contentHint" in track) { - // For audio: "", "speech", "speech-recognition", "music". - // https://w3c.github.io/mst-content-hint/#audio-content-hints - track.contentHint = ""; - logger.info(`[createOffer] Audio track Content Hint set: '${track.contentHint}'`); - } - } else if (track.kind === "video") { - if ("contentHint" in track) { - // For video: "", "motion", "detail", "text". - // https://w3c.github.io/mst-content-hint/#video-content-hints - if (this.configuration.typeOfVideo === TypeOfVideo.SCREEN) { - track.contentHint = "detail"; - } else { - track.contentHint = "motion"; - } - logger.info(`[createOffer] Video track Content Hint set: '${track.contentHint}'`); + 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: number = + trackSettings.width ?? + (trackConsts.width as ConstrainULongRange).ideal ?? + (trackConsts.width as number) ?? + 0; + const trackHeight: number = + trackSettings.height ?? + (trackConsts.height as ConstrainULongRange).ideal ?? + (trackConsts.height as number) ?? + 0; + logger.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; } - if (this.configuration.simulcast) { - // Check if the requested size is enough to ask for 3 layers. - const trackSettings = track.getSettings(); - const trackConsts = track.getConstraints(); + tcInit.sendEncodings = []; + for (let l = 0; l < maxLayers; l++) { + const layerDiv = 2 ** (maxLayers - l - 1); - const trackWidth: number = - trackSettings.width ?? - (trackConsts.width as ConstrainULongRange).ideal ?? - (trackConsts.width as number) ?? - 0; - const trackHeight: number = - trackSettings.height ?? - (trackConsts.height as ConstrainULongRange).ideal ?? - (trackConsts.height as number) ?? - 0; - logger.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`); + const encoding: RTCRtpEncodingParameters = { + rid: "rDiv" + layerDiv.toString(), - const trackPixels = trackWidth * trackHeight; - let maxLayers = 0; - if (trackPixels >= 960 * 540) { - maxLayers = 3; - } else if (trackPixels >= 480 * 270) { - maxLayers = 2; + // @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 { - maxLayers = 1; + encoding.scaleResolutionDownBy = layerDiv; } - tcInit.sendEncodings = []; - for (let l = 0; l < maxLayers; l++) { - const layerDiv = 2 ** (maxLayers - l - 1); - - const encoding: RTCRtpEncodingParameters = { - rid: "rDiv" + layerDiv.toString(), - - // @ts-ignore -- Property missing from DOM types. - scalabilityMode: "L1T1", - }; - - if (this.configuration.typeOfVideo === TypeOfVideo.SCREEN) { - // Prioritize best resolution, for maximum picture detail. - encoding.scaleResolutionDownBy = 1.0; - - // @ts-ignore -- Property missing from DOM types. - encoding.maxFramerate = Math.floor(30 / layerDiv); - // encoding.maxFramerate = (l === 2) ? 30 : Math.floor(30 / (2 * layerDiv)); // TESTING - } else { - encoding.scaleResolutionDownBy = layerDiv; - } - - tcInit.sendEncodings.push(encoding); - } + tcInit.sendEncodings.push(encoding); } } @@ -254,19 +233,16 @@ export class WebRtcPeer { let sendParams = tc.sender.getParameters(); let needSetParams = false; - if (!("degradationPreference" in sendParams)) { - logger.debug(`[createOffer] RTCRtpSendParameters.degradationPreference attribute not present`); - // Asked about why this might happen. Check it: - // https://groups.google.com/g/discuss-webrtc/c/R8Xug-irfRY - - // For video: "balanced", "maintain-framerate", "maintain-resolution". - if (this.configuration.typeOfVideo === TypeOfVideo.SCREEN) { + if (!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"; } - logger.debug( + logger.info( `[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}` );