import BrowserBehavior from '../browserbehavior/BrowserBehavior' ; import Logger from '../logger/Logger' ; import VideoStreamIdSet from '../videostreamidset/VideoStreamIdSet' ; import VideoStreamIndex from '../videostreamindex/VideoStreamIndex' ; import TransceiverController from './TransceiverController' ; export default class DefaultTransceiverController implements TransceiverController { protected _localCameraTransceiver : RTCRtpTransceiver | null = null ; protected _localAudioTransceiver : RTCRtpTransceiver | null = null ; protected videoSubscriptions : number [ ] = [ ] ; protected defaultMediaStream : MediaStream | null = null ; protected peer : RTCPeerConnection | null = null ; protected streamIdToTransceiver : Map < number , RTCRtpTransceiver > = new Map ( ) ; constructor ( protected logger , protected browserBehavior ) { } async setEncodingParameters ( encodingParamMap ) { if ( ! this . _localCameraTransceiver || this . _localCameraTransceiver . direction !== 'sendrecv' ) { return ; } const sender = this . _localCameraTransceiver . sender ; if ( ! encodingParamMap || encodingParamMap . size === 0 ) { return ; } const newEncodingParams = Array . from ( encodingParamMap . values ( ) ) ; const oldParam = sender . getParameters ( ) ; if ( ! oldParam . encodings || oldParam . encodings . length === 0 ) { oldParam . encodings = newEncodingParams ; } else { for ( const existing of oldParam . encodings ) { for ( const changed of newEncodingParams ) { if ( ( existing . rid || changed . rid ) && existing . rid !== changed . rid ) { continue ; } let key ; for ( key in changed ) { if ( key === 'rid' || key === 'codecPayloadType' ) { continue ; } if ( changed . hasOwnProperty ( key ) ) { ( existing [ key ] as RTCRtpEncodingParameters [ keyof RTCRtpEncodingParameters ] ) = changed [ key ] ; } } } } } await sender . setParameters ( oldParam ) ; } static async setVideoSendingBitrateKbpsForSender ( sender , bitrateKbps , _logger ) { if ( ! sender || bitrateKbps <= 0 ) { return ; } const param = sender . getParameters ( ) ; if ( ! param . encodings ) { param . encodings = [ { } ] ; } for ( const encodeParam of param . encodings ) { encodeParam . maxBitrate = bitrateKbps * 1000 ; } await sender . setParameters ( param ) ; } static async replaceAudioTrackForSender ( sender , track ) { if ( ! sender ) { return false ; } await sender . replaceTrack ( track ) ; return true ; } localAudioTransceiver ( ) { return this . _localAudioTransceiver ; } localVideoTransceiver ( ) { return this . _localCameraTransceiver ; } async setVideoSendingBitrateKbps ( bitrateKbps ) { if ( ! this . _localCameraTransceiver || this . _localCameraTransceiver . direction !== 'sendrecv' ) { return ; } const sender = this . _localCameraTransceiver . sender ; await DefaultTransceiverController . setVideoSendingBitrateKbpsForSender ( sender , bitrateKbps , this . logger ) ; } setPeer ( peer ) { this . peer = peer ; } reset ( ) { this . _localCameraTransceiver = null ; this . _localAudioTransceiver = null ; this . videoSubscriptions = [ ] ; this . defaultMediaStream = null ; this . peer = null ; } useTransceivers ( ) { if ( ! this . peer || ! this . browserBehavior . requiresUnifiedPlan ( ) ) { return false ; } return typeof this . peer . getTransceivers !== 'undefined' ; } hasVideoInput ( ) { if ( ! this . _localCameraTransceiver || this . _localCameraTransceiver . direction !== 'sendrecv' ) return false ; return true ; } trackIsVideoInput ( track ) { if ( ! this . _localCameraTransceiver ) { return false ; } return ( track === this . _localCameraTransceiver . sender . track || track === this . _localCameraTransceiver . receiver . track ) ; } setupLocalTransceivers ( ) { if ( ! this . useTransceivers ( ) ) { return ; } if ( ! this . defaultMediaStream && typeof MediaStream !== 'undefined' ) { this . defaultMediaStream = new MediaStream ( ) ; } if ( ! this . _localAudioTransceiver ) { this . _localAudioTransceiver = this . peer . addTransceiver ( 'audio' , { direction : 'inactive' , streams : [ this . defaultMediaStream ] , } ) ; } if ( ! this . _localCameraTransceiver ) { this . _localCameraTransceiver = this . peer . addTransceiver ( 'video' , { direction : 'inactive' , streams : [ this . defaultMediaStream ] , } ) ; } } async replaceAudioTrack ( track ) { if ( ! this . _localAudioTransceiver || this . _localAudioTransceiver . direction !== 'sendrecv' ) { this . logger . info ( `audio transceiver direction is not set up or not activated` ) ; return false ; } await this . _localAudioTransceiver . sender . replaceTrack ( track ) ; return true ; } async setAudioInput ( track ) { await this . setTransceiverInput ( this . _localAudioTransceiver , track ) ; return ; } async setVideoInput ( track ) { await this . setTransceiverInput ( this . _localCameraTransceiver , track ) ; return ; } updateVideoTransceivers ( videoStreamIndex , videosToReceive ) { if ( ! this . useTransceivers ( ) ) { return videosToReceive . array ( ) ; } const transceivers = this . peer . getTransceivers ( ) ; this . videoSubscriptions = [ 0 ] ; videosToReceive = videosToReceive . clone ( ) ; this . updateTransceivers ( transceivers , videoStreamIndex , videosToReceive ) ; this . logger . debug ( ( ) => { return this . debugDumpTransceivers ( ) ; } ) ; return this . videoSubscriptions ; } private updateTransceivers ( transceivers , videoStreamIndex , videosToReceive ) { const videosRemaining = videosToReceive . array ( ) ; let n = 1 ; for ( const transceiver of transceivers ) { if ( transceiver === this . _localCameraTransceiver || ! this . transceiverIsVideo ( transceiver ) ) { continue ; } this . videoSubscriptions [ n ] = 0 ; if ( transceiver . direction !== 'inactive' ) { const streamId = videoStreamIndex . streamIdForTrack ( 'v_' + transceiver . mid ) ; if ( streamId !== undefined ) { for ( const [ index , recvStreamId ] of videosRemaining . entries ( ) ) { if ( videoStreamIndex . StreamIdsInSameGroup ( streamId , recvStreamId ) ) { transceiver . direction = 'recvonly' ; this . videoSubscriptions [ n ] = recvStreamId ; this . streamIdToTransceiver . delete ( streamId ) ; this . streamIdToTransceiver . set ( recvStreamId , transceiver ) ; videosRemaining . splice ( index , 1 ) ; break ; } } } } n += 1 ; } n = 1 ; for ( const transceiver of transceivers ) { if ( transceiver === this . _localCameraTransceiver || ! this . transceiverIsVideo ( transceiver ) ) { continue ; } if ( transceiver . direction === 'inactive' && videosRemaining . length > 0 ) { transceiver . direction = 'recvonly' ; const streamId = videosRemaining . shift ( ) ; this . videoSubscriptions [ n ] = streamId ; this . streamIdToTransceiver . set ( streamId , transceiver ) ; } else { if ( this . videoSubscriptions [ n ] === 0 ) { transceiver . direction = 'inactive' ; for ( const [ streamId , previousTransceiver ] of this . streamIdToTransceiver . entries ( ) ) { if ( transceiver === previousTransceiver ) { this . streamIdToTransceiver . delete ( streamId ) ; } } } } n += 1 ; } for ( const index of videosRemaining ) { const transceiver = this . peer . addTransceiver ( 'video' , { direction : 'recvonly' , streams : [ this . defaultMediaStream ] , } ) ; this . streamIdToTransceiver . set ( index , transceiver ) ; this . videoSubscriptions . push ( index ) ; this . logger . info ( `adding transceiver mid: ${ transceiver . mid } subscription: ${ index } direction: recvonly` ) ; } } getMidForStreamId ( streamId ) { return this . streamIdToTransceiver . get ( streamId ) ?. mid ; } setStreamIdForMid ( mid , newStreamId ) { for ( const [ streamId , transceiver ] of this . streamIdToTransceiver . entries ( ) ) { if ( transceiver . mid === mid ) { this . streamIdToTransceiver . delete ( streamId ) ; this . streamIdToTransceiver . set ( newStreamId , transceiver ) ; return ; } } } protected transceiverIsVideo ( transceiver ) { return ( ( transceiver . receiver && transceiver . receiver . track && transceiver . receiver . track . kind === 'video' ) || ( transceiver . sender && transceiver . sender . track && transceiver . sender . track . kind === 'video' ) ) ; } private debugDumpTransceivers ( ) { let msg = '' ; let n = 0 ; for ( const transceiver of this . peer . getTransceivers ( ) ) { if ( ! this . transceiverIsVideo ( transceiver ) ) { continue ; } msg += `transceiver index=${ n } mid=${ transceiver . mid } subscription=${ this . videoSubscriptions [ n ] } direction=${ transceiver . direction }\n` ; n += 1 ; } return msg ; } private async setTransceiverInput ( transceiver , track ) { if ( ! transceiver ) { return ; } if ( track ) { transceiver . direction = 'sendrecv' ; } else { transceiver . direction = 'inactive' ; } await transceiver . sender . replaceTrack ( track ) ; } }