UNPKG

@observertc/client-monitor-js

Version:

ObserveRTC Client Integration Javascript Library

1 lines 3.93 kB
export class SourceEncoderBottleneckDetector{trackMonitor;static CAPTURE_ISSUE_TYPE="capture-bottleneck";static ENCODER_ISSUE_TYPE="encoder-bottleneck";name="source-encoder-bottleneck-detector";disabled=!1;_captureIssueKey;_encoderIssueKey;_captureTicks=0;_captureOn=!1;_captureStartedAt;_encoderTicks=0;_encoderOn=!1;_encoderStartedAt;constructor(e){this.trackMonitor=e,this._captureIssueKey=`${SourceEncoderBottleneckDetector.CAPTURE_ISSUE_TYPE}-track-${e.track.id}`,this._encoderIssueKey=`${SourceEncoderBottleneckDetector.ENCODER_ISSUE_TYPE}-track-${e.track.id}`}get config(){return this.peerConnection.parent.config.sourceEncoderBottleneckDetector}get peerConnection(){return this.trackMonitor.getPeerConnection()}update(){if(this.disabled)return;if("video"!==this.trackMonitor.kind)return;const e=this.trackMonitor.getMediaSource(),t=this.trackMonitor.getHighestLayer();if(!e||!t)return;if("live"!==this.trackMonitor.track.readyState)return;if(this.trackMonitor.track.muted||!this.trackMonitor.track.enabled)return;if(!1===t.active)return;const r=e.sourceFps??e.framesPerSecond,o=this._expectedFps();this._checkCapture(r,o,e.width,e.height),this._checkEncoder(r,t)}_expectedFps(){try{return this.trackMonitor.track.getSettings?.().frameRate??void 0}catch{return}}_checkCapture(e,t,r,o){if(void 0===e)return;if(!(e<(void 0!==t?t*this.config.captureFpsRatioThreshold:this.config.minSourceFps)))return this._captureTicks=0,void(this._captureOn&&(this._captureOn=!1,this._resolve(this._captureIssueKey,"capture recovered",this._captureStartedAt),this._captureStartedAt=void 0));if(this._captureTicks+=1,this._captureOn)return;if(this._captureTicks<this.config.minConsecutiveTicks)return;this._captureOn=!0,this._captureStartedAt=Date.now();const i=this.peerConnection.parent;i.emit("capture-bottleneck",{clientMonitor:i,trackMonitor:this.trackMonitor,sourceFps:e,expectedFps:t}),i.raiseIssue(this._captureIssueKey,{type:SourceEncoderBottleneckDetector.CAPTURE_ISSUE_TYPE,payload:{peerConnectionId:this.peerConnection.peerConnectionId,trackId:this.trackMonitor.track.id,sourceFps:e,expectedFps:t,sourceWidth:r,sourceHeight:o,consecutiveTicks:this._captureTicks}})}_checkEncoder(e,t){if(!t)return;const r=t.framesPerSecond,o=t.qualityLimitationDurationShares,i=o?.cpu;if(!(void 0!==e&&this.config.minSourceFps<=e))return this._encoderTicks=0,void(this._encoderOn&&(this._encoderOn=!1,this._resolve(this._encoderIssueKey,"source no longer healthy; not an encoder problem",this._encoderStartedAt),this._encoderStartedAt=void 0));const c=void 0!==r&&r<e*this.config.encodeFpsRatioThreshold,n=void 0!==t.encodeTimePerFrameInMs&&void 0!==e&&0<e&&1e3/e*this.config.encodeTimeBudgetRatio<t.encodeTimePerFrameInMs,s=void 0!==i&&this.config.cpuLimitationShareThreshold<i;if(!c&&!n&&!s)return this._encoderTicks=0,void(this._encoderOn&&(this._encoderOn=!1,this._resolve(this._encoderIssueKey,"encoder keeping up again",this._encoderStartedAt),this._encoderStartedAt=void 0));if(this._encoderTicks+=1,this._encoderOn)return;if(this._encoderTicks<this.config.minConsecutiveTicks)return;this._encoderOn=!0,this._encoderStartedAt=Date.now();const a=this.peerConnection.parent;a.emit("encoder-bottleneck",{clientMonitor:a,trackMonitor:this.trackMonitor,sourceFps:e,encodedFps:r}),a.raiseIssue(this._encoderIssueKey,{type:SourceEncoderBottleneckDetector.ENCODER_ISSUE_TYPE,payload:{peerConnectionId:this.peerConnection.peerConnectionId,trackId:this.trackMonitor.track.id,sourceFps:e,encodedFps:r,encodeTimePerFrameInMs:t.encodeTimePerFrameInMs,qualityLimitationReason:t.qualityLimitationReason,cpuLimitationShare:i,encoderImplementation:t.encoderImplementation,powerEfficientEncoder:t.powerEfficientEncoder,consecutiveTicks:this._encoderTicks}})}_resolve(e,t,r){const o=this.peerConnection.parent,i=o.activeIssues.get(e);let c;i&&(c={...i.payload,durationInMs:r?Date.now()-r:void 0}),o.resolveIssue(e,{comment:t,payload:c,resolvedAt:Date.now()})}}