import BrowserBehavior from '../browserbehavior/BrowserBehavior' ; import DefaultBrowserBehavior from '../browserbehavior/DefaultBrowserBehavior' ; import Device from '../devicecontroller/Device' ; import VideoTransformDevice from '../devicecontroller/VideoTransformDevice' ; import Logger from '../logger/Logger' ; import DefaultVideoFrameProcessorPipeline from './DefaultVideoFrameProcessorPipeline' ; import DefaultVideoTransformDeviceObserver from './DefaultVideoTransformDeviceObserver' ; import VideoFrameProcessor from './VideoFrameProcessor' ; import VideoFrameProcessorPipeline from './VideoFrameProcessorPipeline' ; import VideoFrameProcessorPipelineObserver from './VideoFrameProcessorPipelineObserver' ; /**
 * [[DefaultVideoTransformDevice]] is an augmented [[VideoInputDevice]].
 * It transform the input  Device with an array of  VideoFrameProcessor to produce a `MediaStream`.
  export default class DefaultVideoTransformDevice implements VideoTransformDevice , VideoFrameProcessorPipelineObserver { private pipe : VideoFrameProcessorPipeline ; private inputMediaStream : MediaStream ; private observers : Set < DefaultVideoTransformDeviceObserver > = new Set < DefaultVideoTransformDeviceObserver > ( ) ; constructor ( private logger , private device , private processors , private browserBehavior = new DefaultBrowserBehavior ( ) ) { this . pipe = new DefaultVideoFrameProcessorPipeline ( this . logger , this . processors ) ; this . pipe . addObserver ( this ) ; } /**
     * getter for `outputMediaStream`.
     * `outputMediaStream` is returned by internal  VideoFrameProcessorPipeline .
     * It is possible, but unlikely, that this accessor will throw.
      get outputMediaStream ( ) : MediaStream { return this . pipe . outputMediaStream ; } /**
     * `chooseNewInnerDevice` preserves the inner pipeline and processing state and switches
     * the inner device. Since the pipeline and processors are shared with the new transform device
     * only one transform device can be used.
     */ chooseNewInnerDevice ( newDevice ) { const newTransformDevice = new DefaultVideoTransformDevice ( this . logger , newDevice , this . processors , this . browserBehavior ) ; newTransformDevice . pipe = this . pipe ; return newTransformDevice ; } /**
     * Return the inner device as provided during construction.
     */ getInnerDevice ( ) { return this . device ; } async intrinsicDevice ( ) { const trackConstraints = { } ; if ( ! this . device ) { return trackConstraints ; } if ( typeof this . device === 'string' ) { if ( this . browserBehavior . requiresNoExactMediaStreamConstraints ( ) ) { trackConstraints . deviceId = this . device ; } else { trackConstraints . deviceId = { exact : this . device } ; } return trackConstraints ; } if ( ( this . device as MediaStream ) . id ) { return this . device ; } return { ... this . device , ... trackConstraints , } ; } /**
     * Create  VideoFrameProcessorPipeline if there is not a existing one and start video processors.
     * Returns output `MediaStream` produced by  VideoFrameProcessorPipeline .
      async transformStream ( mediaStream ? ) { await this . pipe . setInputMediaStream ( mediaStream ) ; this . inputMediaStream = mediaStream ; return this . pipe . getActiveOutputMediaStream ( ) ; } /**
     * onOutputStreamDisconnect is called when device controller wants to detach
     * the transform device. The default behavior is to stop the output
     * media stream and release the input the media stream. If the input media stream
     * is the provided device, it will not be released.
     */ onOutputStreamDisconnect ( ) { this . logger . info ( 'DefaultVideoTransformDevice: detach stopping input media stream' ) ; const deviceIsMediaStream = this . device && ( this . device as MediaStream ) . id ; this . pipe . stop ( ) ; if ( ! deviceIsMediaStream ) { if ( this . inputMediaStream ) { for ( const track of this . inputMediaStream . getVideoTracks ( ) ) { track . stop ( ) ; } } } } /**
     * Dispose of the inner workings of the transform device, including pipeline and processors.
     * `stop` can only be called when the transform device is not used by device controller anymore.
     * After `stop` is called, all transform devices which share the pipeline must be discarded.
     */ async stop ( ) { if ( this . inputMediaStream ) { for ( const track of this . inputMediaStream . getVideoTracks ( ) ) { track . stop ( ) ; } } this . pipe . destroy ( ) ; this . inputMediaStream = null ; } /**
     * Add an observer to receive notifications about lifecycle events.
     * See  DefaultVideoTransformDeviceObserver for details.
     * If the observer has already been added, this method call has no effect.
      addObserver ( observer ) { this . observers . add ( observer ) ; } /**
     * Remove an existing observer. If the observer has not been previously. this method call has no effect.
     */ removeObserver ( observer ) { this . observers . delete ( observer ) ; } processingDidStart ( ) { this . logger . info ( 'video transform device processing started' ) ; this . forEachObserver ( observer => { if ( observer . processingDidStart ) { observer . processingDidStart ( ) ; } } ) ; } processingLatencyTooHigh ( latencyMs ) { this . forEachObserver ( observer => { if ( observer . processingLatencyTooHigh ) { observer . processingLatencyTooHigh ( latencyMs ) ; } } ) ; } processingDidFailToStart ( ) { this . logger . info ( 'video transform device processing failed to start' ) ; this . forEachObserver ( observer => { if ( observer . processingDidFailToStart ) { observer . processingDidFailToStart ( ) ; } } ) ; } processingDidStop ( ) { this . logger . info ( 'video transform device processing stopped' ) ; this . forEachObserver ( observer => { if ( observer . processingDidStop ) { observer . processingDidStop ( ) ; } } ) ; } private forEachObserver ( observerFunc ) { for ( const observer of this . observers ) { setTimeout ( ( ) => { observerFunc ( observer ) ; } , 0 ) ; } } }