import Logger from '../logger/Logger' ; import { SdkBitrateFrame , SdkIndexFrame , SdkStreamMediaType , SdkSubscribeAckFrame , } from '../signalingprotocol/SignalingProtocol.js' ; import DefaultVideoStreamIndex from './DefaultVideoStreamIndex' ; import VideoStreamDescription from './VideoStreamDescription' ; /**
 * [[SimulcastTransceiverController]] implements [[VideoStreamIndex]] to facilitate video stream
 * subscription and includes query functions for stream id and attendee id.
 */ export default class SimulcastVideoStreamIndex extends DefaultVideoStreamIndex { private streamIdToBitrateKbpsMap : Map < number , number > = new Map < number , number > ( ) ; static readonly UNSEEN_STREAM_BITRATE = - 2 ; static readonly RECENTLY_INACTIVE_STREAM_BITRATE = - 1 ; static readonly NOT_SENDING_STREAM_BITRATE = 0 ; static readonly BitratesMsgFrequencyMs : number = 4000 ; private _localStreamInfos : VideoStreamDescription [ ] = [ ] ; private _lastBitRateMsgTime : number ; constructor ( logger ) { super ( logger ) ; this . _lastBitRateMsgTime = Date . now ( ) ; } localStreamDescriptions ( ) { const clonedDescriptions = [ ] ; this . _localStreamInfos . forEach ( desc => { clonedDescriptions . push ( desc . clone ( ) ) ; } ) ; return clonedDescriptions ; } integrateUplinkPolicyDecision ( encodingParams ) { let hasStreamsToReuse = true ; let localStreamIndex = 0 ; for ( let i = 0 ; i < encodingParams . length ; i ++ ) { const targetMaxBitrateKbps = encodingParams [ i ] . maxBitrate / 1000 ; const targetMaxFrameRate = encodingParams [ i ] . maxFramerate ; if ( ! hasStreamsToReuse || i === this . _localStreamInfos . length ) { hasStreamsToReuse = false ; const newInfo = new VideoStreamDescription ( ) ; newInfo . maxBitrateKbps = targetMaxBitrateKbps ; newInfo . maxFrameRate = targetMaxFrameRate ; newInfo . disabledByUplinkPolicy = targetMaxBitrateKbps === 0 ? true : false ; if ( targetMaxBitrateKbps !== 0 ) { newInfo . timeEnabled = Date . now ( ) ; } this . _localStreamInfos . push ( newInfo ) ; localStreamIndex ++ ; continue ; } if ( this . _localStreamInfos [ localStreamIndex ] . maxBitrateKbps === 0 && targetMaxBitrateKbps > 0 ) { this . _localStreamInfos [ localStreamIndex ] . timeEnabled = Date . now ( ) ; } this . _localStreamInfos [ localStreamIndex ] . maxBitrateKbps = targetMaxBitrateKbps ; this . _localStreamInfos [ localStreamIndex ] . maxFrameRate = targetMaxFrameRate ; this . _localStreamInfos [ localStreamIndex ] . disabledByUplinkPolicy = targetMaxBitrateKbps === 0 ? true : false ; if ( this . _localStreamInfos [ localStreamIndex ] . disabledByUplinkPolicy === true ) { this . _localStreamInfos [ localStreamIndex ] . disabledByWebRTC = false ; } localStreamIndex ++ ; } if ( hasStreamsToReuse ) { this . _localStreamInfos . splice ( localStreamIndex ) ; } } integrateBitratesFrame ( bitrateFrame ) { super . integrateBitratesFrame ( bitrateFrame ) ; const stillSending = new Set < number > ( ) ; const existingSet = new Set < number > ( this . streamIdToBitrateKbpsMap . keys ( ) ) ; for ( const bitrateMsg of bitrateFrame . bitrates ) { stillSending . add ( bitrateMsg . sourceStreamId ) ; this . streamIdToBitrateKbpsMap . set ( bitrateMsg . sourceStreamId , this . convertBpsToKbps ( bitrateMsg . avgBitrateBps ) ) ; } for ( const id of existingSet ) { if ( ! stillSending . has ( id ) ) { const avgBitrateBps = this . streamIdToBitrateKbpsMap . get ( id ) ; if ( avgBitrateBps === SimulcastVideoStreamIndex . UNSEEN_STREAM_BITRATE ) { this . streamIdToBitrateKbpsMap . set ( id , SimulcastVideoStreamIndex . RECENTLY_INACTIVE_STREAM_BITRATE ) ; } else { this . streamIdToBitrateKbpsMap . set ( id , SimulcastVideoStreamIndex . NOT_SENDING_STREAM_BITRATE ) ; } } } for ( let i = 0 ; i < this . _localStreamInfos . length ; i ++ ) { this . _localStreamInfos [ i ] . disabledByWebRTC = false ; const streamId = this . _localStreamInfos [ i ] . streamId ; if ( this . _localStreamInfos [ i ] . disabledByUplinkPolicy ) { continue ; } if ( this . streamIdToBitrateKbpsMap . has ( streamId ) ) { const avgBitrateKbps = this . streamIdToBitrateKbpsMap . get ( streamId ) ; if ( avgBitrateKbps === SimulcastVideoStreamIndex . NOT_SENDING_STREAM_BITRATE && this . _lastBitRateMsgTime - this . _localStreamInfos [ i ] . timeEnabled > SimulcastVideoStreamIndex . BitratesMsgFrequencyMs ) { this . _localStreamInfos [ i ] . disabledByWebRTC = true ; } } else { if ( this . _lastBitRateMsgTime - this . _localStreamInfos [ i ] . timeEnabled > SimulcastVideoStreamIndex . BitratesMsgFrequencyMs ) { this . _localStreamInfos [ i ] . disabledByWebRTC = true ; } } } this . _lastBitRateMsgTime = Date . now ( ) ; this . logLocalStreamDescriptions ( ) ; } private logLocalStreamDescriptions ( ) { let msg = '' ; for ( const desc of this . _localStreamInfos ) { msg += `streamId=${ desc . streamId } maxBitrate=${ desc . maxBitrateKbps } disabledByWebRTC=${ desc . disabledByWebRTC } disabledByUplink=${ desc . disabledByUplinkPolicy }\n` ; } this . logger . debug ( ( ) => { return msg ; } ) ; } integrateIndexFrame ( indexFrame ) { super . integrateIndexFrame ( indexFrame ) ; const newIndexStreamIdSet = new Set < number > ( ) ; const existingSet = new Set < number > ( this . streamIdToBitrateKbpsMap . keys ( ) ) ; for ( const stream of this . currentIndex . sources ) { if ( stream . mediaType !== SdkStreamMediaType . VIDEO ) { continue ; } newIndexStreamIdSet . add ( stream . streamId ) ; if ( ! this . streamIdToBitrateKbpsMap . has ( stream . streamId ) ) { this . streamIdToBitrateKbpsMap . set ( stream . streamId , SimulcastVideoStreamIndex . UNSEEN_STREAM_BITRATE ) ; } } for ( const id of existingSet ) { if ( ! newIndexStreamIdSet . has ( id ) ) { this . streamIdToBitrateKbpsMap . delete ( id ) ; } } } integrateSubscribeAckFrame ( subscribeAck ) { super . integrateSubscribeAckFrame ( subscribeAck ) ; if ( ! subscribeAck . allocations || subscribeAck . allocations === undefined ) { return ; } let localStreamStartIndex = 0 ; for ( const allocation of subscribeAck . allocations ) { if ( this . _localStreamInfos . length < localStreamStartIndex + 1 ) { this . logger . info ( 'simulcast: allocation has more than number of local streams' ) ; break ; } this . _localStreamInfos [ localStreamStartIndex ] . groupId = allocation . groupId ; this . _localStreamInfos [ localStreamStartIndex ] . streamId = allocation . streamId ; if ( ! this . streamIdToBitrateKbpsMap . has ( allocation . streamId ) ) { this . streamIdToBitrateKbpsMap . set ( allocation . streamId , SimulcastVideoStreamIndex . UNSEEN_STREAM_BITRATE ) ; } localStreamStartIndex ++ ; } } }