import { CreateViewOptions , LocalVideoStream , VideoStreamRenderer , VideoStreamRendererView } from '@azure/communication-calling' ; import { CommunicationIdentifierKind } from '@azure/communication-common' ; import { LocalVideoStreamState , RemoteVideoStreamState } from './CallClientState' ; import { CallContext } from './CallContext' ; import { convertSdkLocalStreamToDeclarativeLocalStream , convertSdkRemoteStreamToDeclarativeRemoteStream , convertFromSDKToDeclarativeVideoStreamRendererView } from './Converter' ; import { InternalCallContext } from './InternalCallContext' ; import { toFlatCommunicationIdentifier } from '@internal/acs-ui-common' ; async function createViewRemoteVideo ( context , internalContext , callId , participantId , stream , options ? ) { const streamId = stream . id ; let participantKey ; if ( typeof participantId === 'string' ) { participantKey = participantId ; } else { participantKey = toFlatCommunicationIdentifier ( participantId ) ; } const renderInfo = internalContext . getRemoteRenderInfoForParticipant ( callId , participantKey , streamId ) ; if ( ! renderInfo ) { console . warn ( 'RemoteVideoStream not found in state' ) ; return ; } if ( renderInfo . status === 'Rendered' ) { console . warn ( 'RemoteVideoStream is already rendered' ) ; return ; } if ( renderInfo . status === 'Rendering' ) { return ; } if ( renderInfo . status === 'Stopping' ) { console . warn ( 'RemoteVideoStream is in the middle of stopping' ) ; return ; } const renderer = new VideoStreamRenderer ( renderInfo . stream ) ; internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , renderInfo . stream , 'Rendering' , undefined ) ; let view ; try { view = await renderer . createView ( options ) ; } catch ( e ) { internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , renderInfo . stream , 'NotRendered' , undefined ) ; throw e ; } const refreshedRenderInfo = internalContext . getRemoteRenderInfoForParticipant ( callId , participantKey , streamId ) ; if ( ! refreshedRenderInfo ) { renderer . dispose ( ) ; context . setRemoteVideoStreamRendererView ( callId , participantKey , streamId , undefined ) ; return ; } if ( refreshedRenderInfo . status === 'Stopping' ) { renderer . dispose ( ) ; internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , refreshedRenderInfo . stream , 'NotRendered' , undefined ) ; context . setRemoteVideoStreamRendererView ( callId , participantKey , streamId , undefined ) ; return ; } internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , refreshedRenderInfo . stream , 'Rendered' , renderer ) ; context . setRemoteVideoStreamRendererView ( callId , participantKey , streamId , convertFromSDKToDeclarativeVideoStreamRendererView ( view ) ) ; } async function createViewLocalVideo ( context , internalContext , callId , options ? ) { const renderInfo = internalContext . getLocalRenderInfo ( callId ) ; if ( ! renderInfo ) { console . warn ( 'LocalVideoStream not found in state' ) ; return ; } if ( renderInfo . status === 'Rendered' ) { console . warn ( 'LocalVideoStream is already rendered' ) ; return ; } if ( renderInfo . status === 'Rendering' ) { return ; } if ( renderInfo . status === 'Stopping' ) { console . warn ( 'LocalVideoStream is in the middle of stopping' ) ; return ; } const renderer = new VideoStreamRenderer ( renderInfo . stream ) ; internalContext . setLocalRenderInfo ( callId , renderInfo . stream , 'Rendering' , renderer ) ; let view ; try { view = await renderer . createView ( options ) ; } catch ( e ) { internalContext . setLocalRenderInfo ( callId , renderInfo . stream , 'NotRendered' , undefined ) ; throw e ; } const refreshedRenderInfo = internalContext . getLocalRenderInfo ( callId ) ; if ( ! refreshedRenderInfo ) { renderer . dispose ( ) ; context . setLocalVideoStreamRendererView ( callId , undefined ) ; return ; } if ( refreshedRenderInfo . status === 'Stopping' ) { renderer . dispose ( ) ; internalContext . setLocalRenderInfo ( callId , refreshedRenderInfo . stream , 'NotRendered' , undefined ) ; context . setLocalVideoStreamRendererView ( callId , undefined ) ; return ; } internalContext . setLocalRenderInfo ( callId , refreshedRenderInfo . stream , 'Rendered' , renderer ) ; context . setLocalVideoStreamRendererView ( callId , convertFromSDKToDeclarativeVideoStreamRendererView ( view ) ) ; } async function createViewUnparentedVideo ( context , internalContext , stream , options ? ) { const renderInfo = internalContext . getUnparentedRenderInfo ( stream ) ; if ( renderInfo && renderInfo . status === 'Rendered' ) { console . warn ( 'Unparented LocalVideoStream is already rendered' ) ; return ; } if ( renderInfo && renderInfo . status === 'Rendering' ) { return ; } if ( renderInfo && renderInfo . status === 'Stopping' ) { console . warn ( 'Unparented LocalVideoStream is in the middle of stopping' ) ; return ; } const localVideoStream = new LocalVideoStream ( stream . source ) ; const renderer = new VideoStreamRenderer ( localVideoStream ) ; internalContext . setUnparentedRenderInfo ( stream , localVideoStream , 'Rendering' , undefined ) ; let view ; try { view = await renderer . createView ( options ) ; } catch ( e ) { internalContext . deleteUnparentedRenderInfo ( stream ) ; throw e ; } const refreshedRenderInfo = internalContext . getUnparentedRenderInfo ( stream ) ; if ( ! refreshedRenderInfo ) { renderer . dispose ( ) ; context . deleteDeviceManagerUnparentedView ( stream ) ; return ; } if ( refreshedRenderInfo . status === 'Stopping' ) { internalContext . deleteUnparentedRenderInfo ( stream ) ; context . deleteDeviceManagerUnparentedView ( stream ) ; return ; } internalContext . setUnparentedRenderInfo ( stream , localVideoStream , 'Rendered' , renderer ) ; context . setDeviceManagerUnparentedView ( stream , convertFromSDKToDeclarativeVideoStreamRendererView ( view ) ) ; } function disposeViewRemoteVideo ( context , internalContext , callId , participantId , stream ) { const streamId = stream . id ; let participantKey ; if ( typeof participantId === 'string' ) { participantKey = participantId ; } else { participantKey = toFlatCommunicationIdentifier ( participantId ) ; } context . setRemoteVideoStreamRendererView ( callId , participantKey , streamId , undefined ) ; const renderInfo = internalContext . getRemoteRenderInfoForParticipant ( callId , participantKey , streamId ) ; if ( ! renderInfo ) { return ; } if ( renderInfo . status === 'Rendering' ) { internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , renderInfo . stream , 'Stopping' , undefined ) ; } else { internalContext . setRemoteRenderInfo ( callId , participantKey , streamId , renderInfo . stream , 'NotRendered' , undefined ) ; } if ( renderInfo . renderer ) { renderInfo . renderer . dispose ( ) ; } } function disposeViewLocalVideo ( context , internalContext , callId ) { context . setLocalVideoStreamRendererView ( callId , undefined ) ; const renderInfo = internalContext . getLocalRenderInfo ( callId ) ; if ( ! renderInfo ) { return ; } if ( renderInfo . status === 'Rendering' ) { internalContext . setLocalRenderInfo ( callId , renderInfo . stream , 'Stopping' , undefined ) ; } else { internalContext . setLocalRenderInfo ( callId , renderInfo . stream , 'NotRendered' , undefined ) ; } if ( renderInfo . renderer ) { renderInfo . renderer . dispose ( ) ; } } function disposeViewUnparentedVideo ( context , internalContext , stream ) { context . deleteDeviceManagerUnparentedView ( stream ) ; const renderInfo = internalContext . getUnparentedRenderInfo ( stream ) ; if ( ! renderInfo ) { return ; } if ( renderInfo . status === 'Rendering' ) { internalContext . setUnparentedRenderInfo ( stream , renderInfo . stream , 'Stopping' , undefined ) ; } else { internalContext . deleteUnparentedRenderInfo ( stream ) ; } if ( renderInfo . renderer ) { renderInfo . renderer . dispose ( ) ; } } private export function createView ( context , internalContext , callId , participantId , stream , options ? ) { if ( 'id' in stream && callId && participantId ) { return createViewRemoteVideo ( context , internalContext , callId , participantId , stream , options ) ; } else if ( ! ( 'id' in stream ) && callId ) { return createViewLocalVideo ( context , internalContext , callId , options ) ; } else if ( ! ( 'id' in stream ) && ! callId ) { return createViewUnparentedVideo ( context , internalContext , stream , options ) ; } else { console . warn ( 'Invalid combination of parameters' ) ; return Promise . resolve ( ) ; } } private export function disposeView ( context , internalContext , callId , participantId , stream ) { if ( 'id' in stream && callId && participantId ) { disposeViewRemoteVideo ( context , internalContext , callId , participantId , stream ) ; } else if ( ! ( 'id' in stream ) && callId ) { disposeViewLocalVideo ( context , internalContext , callId ) ; } else if ( ! ( 'id' in stream ) && ! callId ) { disposeViewUnparentedVideo ( context , internalContext , stream ) ; } else { console . warn ( 'Invalid combination of parameters' ) ; return ; } } private export function disposeAllViewsFromCall ( context , internalContext , callId ) { const callStreams = internalContext . getRemoteRenderInfoForCall ( callId ) ; if ( callStreams ) { for ( const [ participantKey , participantStreams ] of callStreams . entries ( ) ) { for ( const [ _ , remoteStreamAndRenderer ] of participantStreams . entries ( ) ) { disposeView ( context , internalContext , callId , participantKey , convertSdkRemoteStreamToDeclarativeRemoteStream ( remoteStreamAndRenderer . stream ) ) ; } } } const localStreamAndRenderer = internalContext . getLocalRenderInfo ( callId ) ; if ( localStreamAndRenderer && localStreamAndRenderer . renderer ) { disposeView ( context , internalContext , callId , undefined , convertSdkLocalStreamToDeclarativeLocalStream ( localStreamAndRenderer . stream ) ) ; } } private export function disposeAllViews ( context , internalContext ) { const remoteStreamAndRenderers = internalContext . getRemoteRenderInfos ( ) ; for ( const [ callId ] of remoteStreamAndRenderers . entries ( ) ) { disposeAllViewsFromCall ( context , internalContext , callId ) ; } }