private async actionConnect()

in src/audiovideocontroller/DefaultAudioVideoController.ts [440:723]


  private async actionConnect(reconnecting: boolean): Promise<void> {
    this.initSignalingClient();

    // We no longer need to watch for the early connection dropping; we're back where
    // we otherwise would have been had we not pre-started.
    this.uninstallPreStartObserver();

    // Note that some of the assignments in this function exist to clean up previous connections.
    // All future 'clean up' assignments should go in `AudioVideoControllerState.resetConnectionSpecificState`
    // for consolidation purposes.

    this.meetingSessionContext.mediaStreamBroker = this._mediaStreamBroker;
    this.meetingSessionContext.realtimeController = this._realtimeController;
    this.meetingSessionContext.audioMixController = this._audioMixController;
    this.meetingSessionContext.audioVideoController = this;

    this.enableSimulcast =
      this.configuration.enableSimulcastForUnifiedPlanChromiumBasedBrowsers &&
      new DefaultBrowserBehavior().hasChromiumWebRTC();

    if (this.enableSimulcast && this.configuration.enableSVC) {
      this.logger.warn(
        'SVC cannot be enabled at the same time as simulcast. Disabling SVC, using simulcast.'
      );
    }
    this.enableSVC =
      !this.enableSimulcast &&
      this.configuration.enableSVC &&
      new DefaultBrowserBehavior().supportsScalableVideoCoding();

    const useAudioConnection: boolean = !!this.configuration.urls.audioHostURL;

    if (!useAudioConnection) {
      this.logger.info(`Using video only transceiver controller`);
      this.meetingSessionContext.transceiverController = new VideoOnlyTransceiverController(
        this.logger,
        this.meetingSessionContext.browserBehavior,
        this.meetingSessionContext
      );
    } else if (this.enableSimulcast) {
      this.logger.info(`Using transceiver controller with simulcast support`);
      if (
        new DefaultModality(this.configuration.credentials.attendeeId).hasModality(
          DefaultModality.MODALITY_CONTENT
        )
      ) {
        this.meetingSessionContext.transceiverController = new SimulcastContentShareTransceiverController(
          this.logger,
          this.meetingSessionContext.browserBehavior,
          this.meetingSessionContext
        );
      } else {
        this.meetingSessionContext.transceiverController = new SimulcastTransceiverController(
          this.logger,
          this.meetingSessionContext.browserBehavior,
          this.meetingSessionContext
        );
      }
    } else {
      this.logger.info(`Using default transceiver controller`);
      this.meetingSessionContext.transceiverController = new DefaultTransceiverController(
        this.logger,
        this.meetingSessionContext.browserBehavior,
        this.meetingSessionContext
      );
    }

    this.meetingSessionContext.volumeIndicatorAdapter = new DefaultVolumeIndicatorAdapter(
      this.logger,
      this._realtimeController,
      DefaultAudioVideoController.MIN_VOLUME_DECIBELS,
      DefaultAudioVideoController.MAX_VOLUME_DECIBELS,
      this.configuration.credentials.attendeeId
    );
    this.meetingSessionContext.videoTileController = this._videoTileController;
    this.meetingSessionContext.videoDownlinkBandwidthPolicy = this.configuration.videoDownlinkBandwidthPolicy;
    this.meetingSessionContext.videoUplinkBandwidthPolicy = this.configuration.videoUplinkBandwidthPolicy;
    this.meetingSessionContext.enableSimulcast = this.enableSimulcast;
    this.meetingSessionContext.enableSVC = this.enableSVC;

    if (this.enableSimulcast) {
      let simulcastPolicy = this.meetingSessionContext
        .videoUplinkBandwidthPolicy as SimulcastUplinkPolicy;
      if (!simulcastPolicy) {
        simulcastPolicy = new DefaultSimulcastUplinkPolicy(
          this.configuration.credentials.attendeeId,
          this.meetingSessionContext.logger
        );
        this.meetingSessionContext.videoUplinkBandwidthPolicy = simulcastPolicy;
      }

      simulcastPolicy.addObserver(this);

      if (!this.meetingSessionContext.videoDownlinkBandwidthPolicy) {
        this.meetingSessionContext.videoDownlinkBandwidthPolicy = new VideoAdaptiveProbePolicy(
          this.meetingSessionContext.logger
        );
      }

      this.meetingSessionContext.videoStreamIndex = new SimulcastVideoStreamIndex(this.logger);
    } else {
      this.meetingSessionContext.enableSimulcast = false;
      this.meetingSessionContext.videoStreamIndex = new DefaultVideoStreamIndex(this.logger);

      if (!this.meetingSessionContext.videoUplinkBandwidthPolicy) {
        this.meetingSessionContext.videoUplinkBandwidthPolicy = new NScaleVideoUplinkBandwidthPolicy(
          this.configuration.credentials.attendeeId,
          !this.meetingSessionContext.browserBehavior.disableResolutionScaleDown(),
          this.meetingSessionContext.logger,
          this.meetingSessionContext.browserBehavior
        );
        this.meetingSessionContext.videoUplinkBandwidthPolicy.setSVCEnabled(this.enableSVC);
      }
      if (!this.meetingSessionContext.videoDownlinkBandwidthPolicy) {
        this.meetingSessionContext.videoDownlinkBandwidthPolicy = new AllHighestVideoBandwidthPolicy(
          this.configuration.credentials.attendeeId
        );
      }

      if (
        this.meetingSessionContext.videoUplinkBandwidthPolicy.setTransceiverController &&
        this.meetingSessionContext.videoUplinkBandwidthPolicy.updateTransceiverController
      ) {
        this.useUpdateTransceiverControllerForUplink = true;
        this.meetingSessionContext.videoUplinkBandwidthPolicy.setTransceiverController(
          this.meetingSessionContext.transceiverController
        );
      }
      this.meetingSessionContext.audioProfile = this._audioProfile;
    }

    if (
      new DefaultModality(this.configuration.credentials.attendeeId).hasModality(
        DefaultModality.MODALITY_CONTENT
      )
    ) {
      const enableUhdContent =
        this.configuration.meetingFeatures.contentMaxResolution ===
        VideoQualitySettings.VideoResolutionUHD;
      if (enableUhdContent) {
        // Increase default bandwidth for content share since this is not yet configuration that can be exposed
        // without using simulcast
        this.setVideoMaxBandwidthKbps(2500);
      }
      this.meetingSessionContext.videoUplinkBandwidthPolicy.setHighResolutionFeatureEnabled(
        enableUhdContent
      );
    } else {
      const enableFhdVideo =
        this.configuration.meetingFeatures.videoMaxResolution ===
        VideoQualitySettings.VideoResolutionFHD;
      this.meetingSessionContext.videoUplinkBandwidthPolicy.setHighResolutionFeatureEnabled(
        enableFhdVideo
      );
    }

    if (this.meetingSessionContext.videoUplinkBandwidthPolicy && this.maxUplinkBandwidthKbps) {
      this.meetingSessionContext.videoUplinkBandwidthPolicy.setIdealMaxBandwidthKbps(
        this.maxUplinkBandwidthKbps
      );
    }

    if (this.meetingSessionContext.videoDownlinkBandwidthPolicy.bindToTileController) {
      this.meetingSessionContext.videoDownlinkBandwidthPolicy.bindToTileController(
        this._videoTileController
      );
    }

    if (this.meetingSessionContext.videoDownlinkBandwidthPolicy.setWantsResubscribeObserver) {
      this.meetingSessionContext.videoDownlinkBandwidthPolicy.setWantsResubscribeObserver(() =>
        this.update({ needsRenegotiation: false })
      );
    }

    this.meetingSessionContext.lastKnownVideoAvailability = new MeetingSessionVideoAvailability();
    this.meetingSessionContext.videoCaptureAndEncodeParameter = new DefaultVideoCaptureAndEncodeParameter(
      0,
      0,
      0,
      0,
      false
    );
    this.meetingSessionContext.videosToReceive = new DefaultVideoStreamIdSet();
    this.meetingSessionContext.videosPaused = new DefaultVideoStreamIdSet();
    this.meetingSessionContext.statsCollector = new StatsCollector(this, this.logger);
    this.meetingSessionContext.connectionMonitor = new SignalingAndMetricsConnectionMonitor(
      this,
      this._realtimeController,
      this.connectionHealthData,
      new DefaultPingPong(
        this.meetingSessionContext.signalingClient,
        DefaultAudioVideoController.PING_PONG_INTERVAL_MS,
        this.logger
      ),
      this.meetingSessionContext.statsCollector
    );
    this.meetingSessionContext.reconnectController = this._reconnectController;
    this.meetingSessionContext.videoDeviceInformation = {};
    this._videoTileController.registerVideoTileResolutionObserver(
      this.meetingSessionContext.statsCollector
    );

    if (!reconnecting) {
      this.totalRetryCount = 0;
      this._reconnectController.reset();
      this.startAudioVideoTimestamp = Date.now();
      this.forEachObserver(observer => {
        Maybe.of(observer.audioVideoDidStartConnecting).map(f => f.bind(observer)(false));
      });
      this.eventController?.publishEvent('meetingStartRequested');
    }

    this.meetingSessionContext.startAudioVideoTimestamp = this.startAudioVideoTimestamp;
    if (this._reconnectController.hasStartedConnectionAttempt()) {
      // This does not reset the reconnect deadline, but declare it's not the first connection.
      this._reconnectController.startedConnectionAttempt(false);
    } else {
      this._reconnectController.startedConnectionAttempt(true);
    }

    // No attendee presence event will be triggered if there is no audio connection.
    // Waiting for attendee presence is explicitly executed
    // if `attendeePresenceTimeoutMs` is configured to larger than 0.
    const needsToWaitForAttendeePresence =
      useAudioConnection &&
      this.meetingSessionContext.meetingSessionConfiguration.attendeePresenceTimeoutMs > 0;

    this.logger.info('Needs to wait for attendee presence? ' + needsToWaitForAttendeePresence);

    const connect = this.connectWithPromises(needsToWaitForAttendeePresence);

    // The rest.
    try {
      await connect.run();

      this.connectionHealthData.setConnectionStartTime();
      const isContentShare = new DefaultModality(
        this.configuration.credentials.attendeeId
      ).hasModality(DefaultModality.MODALITY_CONTENT);
      this.connectionHealthData.setIsContentShare(isContentShare);
      this._mediaStreamBroker.addMediaStreamBrokerObserver(this);
      this.sessionStateController.perform(SessionStateControllerAction.FinishConnecting, () => {
        /* istanbul ignore else */
        if (this.eventController) {
          this.meetingSessionContext.meetingStartDurationMs =
            Date.now() - this.startAudioVideoTimestamp;
          this.eventController.publishEvent('meetingStartSucceeded', {
            maxVideoTileCount: this.meetingSessionContext.maxVideoTileCount,
            poorConnectionCount: this.meetingSessionContext.poorConnectionCount,
            retryCount: this.totalRetryCount,
            signalingOpenDurationMs: this.meetingSessionContext.signalingOpenDurationMs,
            iceGatheringDurationMs: this.meetingSessionContext.iceGatheringDurationMs,
            meetingStartDurationMs: this.meetingSessionContext.meetingStartDurationMs,
          });
        }
        this.meetingSessionContext.startTimeMs = Date.now();
        this.actionFinishConnecting();
      });
    } catch (error) {
      this.signalingTask = undefined;
      const status = new MeetingSessionStatus(
        this.getMeetingStatusCode(error) || MeetingSessionStatusCode.TaskFailed
      );
      this.logger.info(`Start failed: ${status} due to error ${error}.`);

      // I am not able to successfully reach this state in the test suite with mock
      // websockets -- it always ends up in 'Disconnecting' instead. As such, this
      // has to be marked for Istanbul.
      /* istanbul ignore if */
      if (this.sessionStateController.state() === SessionStateControllerState.NotConnected) {
        // There's no point trying to 'disconnect', because we're not connected.
        // The session state controller will bail.
        this.logger.info('Start failed and not connected. Not cleaning up.');
        return;
      }

      this.sessionStateController.perform(SessionStateControllerAction.Fail, async () => {
        await this.actionDisconnect(status, true, error);
        if (!this.handleMeetingSessionStatus(status, error)) {
          this.notifyStop(status, error);
        }
      });
    }
  }