private async actionConnect()

in src/audiovideocontroller/DefaultAudioVideoController.ts [455:676]


  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();

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

    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
      );
    } else if (this.enableSimulcast) {
      this.logger.info(`Using transceiver controller with simulcast support`);
      this.meetingSessionContext.transceiverController = new SimulcastTransceiverController(
        this.logger,
        this.meetingSessionContext.browserBehavior
      );
    } else {
      this.logger.info(`Using default transceiver controller`);
      this.meetingSessionContext.transceiverController = new DefaultTransceiverController(
        this.logger,
        this.meetingSessionContext.browserBehavior
      );
    }

    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;

    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
        );
      }
      if (!this.meetingSessionContext.videoDownlinkBandwidthPolicy) {
        this.meetingSessionContext.videoDownlinkBandwidthPolicy = new AllHighestVideoBandwidthPolicy(
          this.configuration.credentials.attendeeId
        );
      }

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

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

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

    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 DefaultStatsCollector(
      this,
      this.logger,
      this.meetingSessionContext.browserBehavior
    );
    this.meetingSessionContext.connectionMonitor = new SignalingAndMetricsConnectionMonitor(
      this,
      this._realtimeController,
      this._videoTileController,
      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 = {};

    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));
      });
      /* istanbul ignore else */
      if (this.eventController) {
        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);
    let connect: Task;
    if (this.usePromises) {
      connect = this.connectWithPromises(needsToWaitForAttendeePresence);
    } else {
      connect = this.connectWithTasks(needsToWaitForAttendeePresence);
    }

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

      this.connectionHealthData.setConnectionStartTime();
      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);
        }
      });
    }
  }