async function createViewVideo()

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
  };
}