in packages/calling-stateful-client/src/StreamUtils.ts [34:260]
async function createViewVideo(
context: CallContext,
internalContext: InternalCallContext,
callId: string,
stream?: RemoteVideoStreamState | LocalVideoStreamState,
participantId?: CommunicationIdentifierKind | string,
options?: CreateViewOptions
): Promise<CreateViewResult | undefined> {
// we can only have 3 types of createView
let streamEventType: 'createViewLocal' | 'createViewRemote' | 'createViewUnparented';
// we will reuse these for local as well but we need to make sure the remote stream is passed in like before.
if (participantId) {
streamEventType = 'createViewRemote';
} else if (callId) {
streamEventType = 'createViewLocal';
} else {
// TODO update for when unparented view.
throw new Error('unparented createView not implemented yet here');
streamEventType = 'createViewUnparented';
}
const streamType = stream?.mediaStreamType;
const localStreamKey = (stream as LocalVideoStream).mediaStreamType;
const remoteStreamId = (stream as RemoteVideoStream).id;
// we want to check to see if there is a participantId this will tell us whether its a local stream or a remote one.
const participantKey =
streamEventType === 'createViewRemote' && participantId
? typeof participantId === 'string'
? participantId
: toFlatCommunicationIdentifier(participantId)
: undefined;
const streamLogInfo = {
callId,
participantKey,
streamId: remoteStreamId ?? localStreamKey,
streamType,
streamEventType
};
// make different logging announcement based on whether or not we are starting a local or remote
_logStreamEvent(EventNames.CREATING_VIEW, streamLogInfo);
// if we have a participant Id and a stream get the remote info, else get the local render info from state.
const renderInfo =
streamEventType === 'createViewRemote' && participantKey
? internalContext.getRemoteRenderInfoForParticipant(callId, participantKey, remoteStreamId)
: internalContext.getLocalRenderInfo(callId, localStreamKey);
if (!renderInfo) {
_logStreamEvent(EventNames.STREAM_NOT_FOUND, streamLogInfo);
return;
}
if (renderInfo.status === 'Rendered') {
_logStreamEvent(EventNames.STREAM_ALREADY_RENDERED, streamLogInfo);
return;
}
if (renderInfo.status === 'Rendering') {
// Do not log to console here as this is a very common situation due to UI rerenders while
// the video rendering is in progress.
_logStreamEvent(EventNames.STREAM_RENDERING, streamLogInfo);
return;
}
// "Stopping" only happens if the stream was in "rendering" but `disposeView` was called.
// Now that `createView` has been re-called, we can flip the state back to "rendering".
if (renderInfo.status === 'Stopping') {
if (streamEventType === 'createViewRemote' && participantKey) {
_logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo);
internalContext.setRemoteRenderInfo(
callId,
participantKey,
remoteStreamId,
renderInfo.stream as RemoteVideoStream,
'Rendering',
renderInfo.renderer
);
} else if (streamEventType === 'createViewLocal') {
_logStreamEvent(EventNames.STREAM_STOPPING, streamLogInfo);
internalContext.setLocalRenderInfo(
callId,
localStreamKey,
renderInfo.stream as LocalVideoStream,
'Rendering',
renderInfo.renderer
);
}
return;
}
const renderer = new VideoStreamRenderer(renderInfo.stream);
streamEventType === 'createViewRemote' && participantKey
? internalContext.setRemoteRenderInfo(
callId,
participantKey,
remoteStreamId,
renderInfo.stream as RemoteVideoStream,
'Rendering',
undefined
)
: internalContext.setLocalRenderInfo(
callId,
localStreamKey,
renderInfo.stream as LocalVideoStream,
'Rendering',
renderer
);
let view;
try {
view = await renderer.createView(options);
} catch (e) {
if (streamEventType === 'createViewRemote' && participantKey) {
_logStreamEvent(EventNames.CREATE_STREAM_FAIL, streamLogInfo);
internalContext.setRemoteRenderInfo(
callId,
participantKey,
remoteStreamId,
renderInfo.stream as RemoteVideoStream,
'NotRendered',
undefined
);
} else if (streamEventType === 'createViewLocal') {
_logStreamEvent(EventNames.CREATE_STREAM_FAIL, streamLogInfo, e);
internalContext.setLocalRenderInfo(
callId,
localStreamKey,
renderInfo.stream as LocalVideoStream,
'NotRendered',
undefined
);
}
throw e;
}
// Since render could take some time, we need to check if the stream is still valid and if we received a signal to
// stop rendering.
const refreshedRenderInfo =
streamEventType === 'createViewRemote' && participantKey
? internalContext.getRemoteRenderInfoForParticipant(callId, participantKey, remoteStreamId)
: internalContext.getLocalRenderInfo(callId, localStreamKey);
if (!refreshedRenderInfo) {
// RenderInfo was removed. This should not happen unless stream was removed from the call so dispose the renderer
// and clean up the state.
_logStreamEvent(EventNames.RENDER_INFO_NOT_FOUND, streamLogInfo);
renderer.dispose();
streamEventType === 'createViewRemote' && participantKey
? context.setRemoteVideoStreamRendererView(callId, participantKey, remoteStreamId, undefined)
: context.setLocalVideoStreamRendererView(callId, localStreamKey, undefined);
return;
}
if (refreshedRenderInfo.status === 'Stopping') {
// Stop render was called on this stream after we had started rendering. We will dispose this view and do not
// put the view into the state.
streamEventType === 'createViewRemote';
_logStreamEvent(EventNames.CREATED_STREAM_STOPPING, streamLogInfo);
renderer.dispose();
if (streamEventType === 'createViewRemote' && participantKey) {
internalContext.setRemoteRenderInfo(
callId,
participantKey,
remoteStreamId,
refreshedRenderInfo.stream as RemoteVideoStream,
'NotRendered',
undefined
);
context.setRemoteVideoStreamRendererView(callId, participantKey, remoteStreamId, undefined);
} else if (streamEventType === 'createViewLocal') {
internalContext.setLocalRenderInfo(
callId,
localStreamKey,
refreshedRenderInfo.stream as LocalVideoStream,
'NotRendered',
undefined
);
context.setLocalVideoStreamRendererView(callId, localStreamKey, undefined);
}
return;
}
// Else the stream still exists and status is not telling us to stop rendering. Complete the render process by
// updating the state.
if (streamEventType === 'createViewRemote' && participantKey) {
internalContext.setRemoteRenderInfo(
callId,
participantKey,
remoteStreamId,
refreshedRenderInfo.stream as RemoteVideoStream,
'Rendered',
renderer
);
context.setRemoteVideoStreamRendererView(
callId,
participantKey,
remoteStreamId,
convertFromSDKToDeclarativeVideoStreamRendererView(view)
);
_logStreamEvent(EventNames.VIEW_RENDER_SUCCEED, streamLogInfo);
} else if (streamEventType === 'createViewLocal') {
internalContext.setLocalRenderInfo(
callId,
localStreamKey,
refreshedRenderInfo.stream as LocalVideoStream,
'Rendered',
renderer
);
context.setLocalVideoStreamRendererView(
callId,
localStreamKey,
convertFromSDKToDeclarativeVideoStreamRendererView(view)
);
_logStreamEvent(EventNames.VIEW_RENDER_SUCCEED, streamLogInfo);
}
return {
renderer,
view
};
}