import { clContext as nodenCLContext } from 'nodencl' ; import { LoadParams } from './chanLayer' ; import { Producer , ProducerRegistry } from './producer/producer' ; import { ConsumerConfig } from './config' ; import { DefaultTransitionSpec , Layer } from './layer' ; import { ConsumerRegistry , Consumer } from './consumer/consumer' ; import { CombineLayer , Combiner } from './combiner' ; import { ClJobs } from './clJobQueue' ; import { TransitionSpec } from './transitioner' ; import { SourcePipes } from './routeSource' ; export class Channel { private readonly clContext : nodenCLContext ; private readonly chanID : string ; private readonly consumerConfig : ConsumerConfig ; private readonly consumerRegistry : ConsumerRegistry ; private readonly producerRegistry : ProducerRegistry ; private readonly clJobs : ClJobs ; private readonly combiner : Combiner ; private readonly consumers : Consumer [ ] ; private readonly layers : Map < number , Layer > ; constructor ( clContext , chanID , chanNum , consumerConfig , consumerRegistry , producerRegistry , clJobs ) { this . clContext = clContext ; this . chanID = chanID ; this . consumerConfig = consumerConfig ; this . consumerRegistry = consumerRegistry ; this . producerRegistry = producerRegistry ; this . clJobs = clJobs ; this . combiner = new Combiner ( this . clContext , this . chanID , this . consumerConfig . format , this . clJobs ) ; this . consumers = this . consumerRegistry . createConsumers ( chanNum , this . chanID , this . consumerConfig , this . clJobs ) ; this . layers = new Map < number , Layer > ( ) ; } async initialise ( ) { await this . combiner . initialise ( ) ; await Promise . all ( this . consumers . map ( ( c ) => c . initialise ( ) ) ) ; this . consumers . forEach ( ( c ) => this . addConsumer ( c ) ) ; return Promise . resolve ( ) ; } getConfig ( ) { return this . consumerConfig ; } addConsumer ( consumer ) { if ( ! this . consumers . find ( ( c ) => c === consumer ) ) this . consumers . push ( consumer ) ; this . combiner . connect ( consumer ) ; } removeConsumer ( consumer ) { if ( ! this . consumers . some ( ( c ) => c === consumer ) ) throw new Error ( 'remove consumer - consumer not found' ) ; const i = this . consumers . indexOf ( consumer ) ; this . consumers . splice ( i , 1 ) ; this . combiner . release ( consumer ) ; } updateLayers ( ) { const layerNums = [ ] ; const layerIter = this . layers . keys ( ) ; let next = layerIter . next ( ) ; while ( ! next . done ) { layerNums . push ( next . value ) ; next = layerIter . next ( ) ; } layerNums . sort ( ( a , b ) => a - b ) ; const curLayers = this . combiner . getLayers ( ) ; const newLayers = [ ] ; layerNums . forEach ( ( l ) => { const layer = this . layers . get ( l ) ; if ( layer ) { const audPipe = layer . getAudioPipe ( ) ; const vidPipe = layer . getVideoPipe ( ) ; if ( audPipe && vidPipe ) { const aid = audPipe . fittingId ; const curLayer = curLayers . find ( ( cl ) => aid === cl . getAudioPipe ( ) . fittingId ) ; if ( curLayer ) newLayers . push ( curLayer ) ; newLayers . push ( new CombineLayer ( layer , audPipe , vidPipe ) ) ; } } } ) ; this . combiner . updateLayers ( newLayers ) ; } async loadSource ( params ) { let producer ; const transitionSpec = JSON . parse ( DefaultTransitionSpec ) ; let error = '' ; try { producer = await this . producerRegistry . createSource ( params , this . consumerConfig . format , this . clJobs ) ; if ( params . transition ) { transitionSpec . type = params . transition . type ; if ( params . transition . type === 'wipe' && params . transition . url ) transitionSpec . mask = await this . producerRegistry . createSource ( { url : params . transition . url , streams : params . transition . streams , layer : params . layer , length : params . transition . length } , this . consumerConfig . format , this . clJobs ) ; transitionSpec . len = params . transition . length ; } } catch ( err ) { if ( typeof err === 'string' ) error = err ; else error = err instanceof Error ? err . message : 'Unknown error' ; } if ( ! producer || error . length > 0 ) { console . log ( 'Failed to create source for params' , params , error ) ; return false ; } let layer = this . layers . get ( params . layer ) ; if ( ! layer ) { layer = new Layer ( this . clContext , `${ this . chanID }-l${ params . layer }` , this . consumerConfig , this . clJobs ) ; await layer . initialise ( ) ; this . layers . set ( params . layer , layer ) ; } return layer . load ( producer , transitionSpec , params . preview ? true : false , params . autoPlay ? true : false , this . updateLayers . bind ( this ) ) ; } async play ( layerNum , ticker ? ) { const layer = this . layers . get ( layerNum ) ; await layer ?. play ( async ( t ) => { if ( t === 'end' ) { await layer ?. release ( ) ; this . layers . delete ( layerNum ) ; this . updateLayers ( ) ; } else if ( ticker ) ticker ( ) ; } ) ; return layer !== undefined ; } pause ( layerNum ) { const layer = this . layers . get ( layerNum ) ; layer ?. pause ( ) ; return layer !== undefined ; } resume ( layerNum ) { const layer = this . layers . get ( layerNum ) ; layer ?. resume ( ) ; return layer !== undefined ; } async stop ( layerNum ) { const layer = this . layers . get ( layerNum ) ; await layer ?. stop ( ) ; return layer !== undefined ; } async clear ( layerNum ) { let result = true ; if ( layerNum === 0 ) { const layerNums = [ ] ; const layerIter = this . layers . keys ( ) ; let next = layerIter . next ( ) ; while ( ! next . done ) { layerNums . push ( next . value ) ; next = layerIter . next ( ) ; } await Promise . all ( layerNums . map ( ( n ) => this . stop ( n ) . then ( ( ) => this . layers . get ( n ) ?. release ( ) ) ) ) ; for ( const n of layerNums ) this . layers . delete ( n ) ; } else { result = await this . stop ( layerNum ) ; await this . layers . get ( layerNum ) ?. release ( ) ; this . layers . delete ( layerNum ) ; } this . updateLayers ( ) ; return result ; } anchor ( layerNum , params ) { const layer = this . layers . get ( layerNum ) ; layer ?. anchor ( params ) ; return layer !== undefined ; } rotation ( layerNum , params ) { const layer = this . layers . get ( layerNum ) ; layer ?. rotation ( params ) ; return layer !== undefined ; } fill ( layerNum , params ) { const layer = this . layers . get ( layerNum ) ; layer ?. fill ( params ) ; return layer !== undefined ; } volume ( layerNum , params ) { const layer = this . layers . get ( layerNum ) ; layer ?. volume ( params ) ; return layer !== undefined ; } async getRoutePipes ( layerNum ) { let sourcePipes ; if ( layerNum === 0 ) { sourcePipes = await this . combiner . getSourcePipes ( ) ; } else { const layer = this . layers . get ( layerNum ) ; sourcePipes = await layer ?. getSourcePipes ( ) ; } if ( ! sourcePipes ) throw new Error ( `Failed to find source pipes for layer ${ layerNum }` ) ; return sourcePipes ; } }