import Logger from '../logger/Logger' ; import TransceiverController from '../transceivercontroller/TransceiverController' ; import DefaultVideoAndEncodeParameter from '../videocaptureandencodeparameter/DefaultVideoCaptureAndEncodeParameter' ; import VideoStreamIndex from '../videostreamindex/VideoStreamIndex' ; import ConnectionMetrics from './ConnectionMetrics' ; import VideoUplinkBandwidthPolicy from './VideoUplinkBandwidthPolicy' ; /** NScaleVideoUplinkBandwidthPolicy implements capture and encode
 *  parameters that are nearly equivalent to those chosen by the
 *  traditional native clients, except for a modification to
 *  maxBandwidthKbps and scaleResolutionDownBy described below. */ export default class NScaleVideoUplinkBandwidthPolicy implements VideoUplinkBandwidthPolicy { static readonly encodingMapKey = 'video' ; static readonly targetHeightArray = [ 0 , 0 , 0 , 540 , 540 , 480 , 480 , 480 , 480 , 360 , 360 , 360 , 360 , 270 , 270 , 270 , 270 , 180 , 180 , 180 , 180 , 180 , 180 , 180 , 180 , 180 , ] ; private numParticipants : number = 0 ; private optimalParameters : DefaultVideoAndEncodeParameter ; private parametersInEffect : DefaultVideoAndEncodeParameter ; private idealMaxBandwidthKbps = 1400 ; private hasBandwidthPriority : boolean = false ; private encodingParamMap = new Map < string , RTCRtpEncodingParameters > ( ) ; private transceiverController : TransceiverController ; constructor ( private selfAttendeeId , private scaleResolution = true , private logger = undefined ) { this . optimalParameters = new DefaultVideoAndEncodeParameter ( 0 , 0 , 0 , 0 , false ) ; this . parametersInEffect = new DefaultVideoAndEncodeParameter ( 0 , 0 , 0 , 0 , false ) ; this . encodingParamMap . set ( NScaleVideoUplinkBandwidthPolicy . encodingMapKey , { maxBitrate : 0 , } ) ; } updateConnectionMetric ( _metrics ) { return ; } chooseMediaTrackConstraints ( ) { return { } ; } chooseEncodingParameters ( ) { return new Map < string , RTCRtpEncodingParameters > ( ) ; } updateIndex ( videoIndex ) { let hasLocalVideo = true ; let scale = 1 ; if ( this . transceiverController ) { hasLocalVideo = this . transceiverController . hasVideoInput ( ) ; } this . numParticipants = videoIndex . numberOfVideoPublishingParticipantsExcludingSelf ( this . selfAttendeeId ) + ( hasLocalVideo ? 1 : 0 ) ; if ( this . transceiverController ) { const settings = this . getStreamCaptureSetting ( ) ; if ( settings ) { const encodingParams = this . calculateEncodingParameters ( settings ) ; scale = encodingParams . scaleResolutionDownBy ; } } this . optimalParameters = new DefaultVideoAndEncodeParameter ( this . captureWidth ( ) , this . captureHeight ( ) , this . captureFrameRate ( ) , this . maxBandwidthKbps ( ) , false , scale ) ; } wantsResubscribe ( ) { return ! this . parametersInEffect . equal ( this . optimalParameters ) ; } chooseCaptureAndEncodeParameters ( ) { this . parametersInEffect = this . optimalParameters . clone ( ) ; return this . parametersInEffect . clone ( ) ; } private captureWidth ( ) { let width = 640 ; if ( this . numParticipants > 4 ) { width = 320 ; } return width ; } private captureHeight ( ) { let height = 384 ; if ( this . numParticipants > 4 ) { height = 192 ; } return height ; } private captureFrameRate ( ) { return 15 ; } maxBandwidthKbps ( ) { if ( this . hasBandwidthPriority ) { return Math . trunc ( this . idealMaxBandwidthKbps ) ; } let rate = 0 ; if ( this . numParticipants <= 2 ) { rate = this . idealMaxBandwidthKbps ; } else if ( this . numParticipants <= 4 ) { rate = ( this . idealMaxBandwidthKbps * 2 ) / 3 ; } else { rate = ( ( 544 / 11 + 14880 / ( 11 * this . numParticipants ) ) / 600 ) * this . idealMaxBandwidthKbps ; } return Math . trunc ( rate ) ; } setIdealMaxBandwidthKbps ( idealMaxBandwidthKbps ) { this . idealMaxBandwidthKbps = idealMaxBandwidthKbps ; } setHasBandwidthPriority ( hasBandwidthPriority ) { this . hasBandwidthPriority = hasBandwidthPriority ; } setTransceiverController ( transceiverController ) { this . transceiverController = transceiverController ; } async updateTransceiverController ( ) { const settings = this . getStreamCaptureSetting ( ) ; if ( ! settings ) { return ; } const encodingParams = this . calculateEncodingParameters ( settings ) ; if ( ! this . encodingParametersHaveSameBitrateAndScaling ( encodingParams , this . encodingParamMap . get ( NScaleVideoUplinkBandwidthPolicy . encodingMapKey ) ) ) { this . encodingParamMap . set ( NScaleVideoUplinkBandwidthPolicy . encodingMapKey , encodingParams ) ; this . transceiverController . setEncodingParameters ( this . encodingParamMap ) ; } } private encodingParametersHaveSameBitrateAndScaling ( encoding1 , encoding2 ) { return ( encoding1 === encoding2 || ( encoding1 . maxBitrate === encoding2 . maxBitrate && encoding1 . scaleResolutionDownBy === encoding2 . scaleResolutionDownBy ) ) ; } private calculateEncodingParameters ( setting ) { const maxBitrate = this . maxBandwidthKbps ( ) * 1000 ; let scale = 1 ; if ( setting . height !== undefined && setting . width !== undefined && this . scaleResolution && ! this . hasBandwidthPriority && this . numParticipants > 2 ) { const targetHeight = NScaleVideoUplinkBandwidthPolicy . targetHeightArray [ Math . min ( this . numParticipants , NScaleVideoUplinkBandwidthPolicy . targetHeightArray . length - 1 ) ] ; scale = Math . max ( Math . min ( setting . height , setting . width ) / targetHeight , 1 ) ; this . logger ?. info ( `Resolution scale factor is ${ scale } for capture resolution ${ setting . width }x${ setting . height }. New dimension is ${ setting . width / scale }x${ setting . height / scale }` ) ; } return { scaleResolutionDownBy : scale , maxBitrate : maxBitrate , } ; } private getStreamCaptureSetting ( ) { return this . transceiverController ?. localVideoTransceiver ( ) ?. sender ?. track ?. getSettings ( ) ; } }