joinRoom method

dynamic joinRoom(
  1. RoomInfo roomInfo, {
  2. dynamic micEnabled = false,
  3. dynamic cameraEnabled = false,
  4. bool? screenShareEnabled,
  5. bool? scrennShareEnabled,
})

Implementation

joinRoom(RoomInfo roomInfo,
    {micEnabled = false,
    cameraEnabled = false,
    bool? screenShareEnabled,
    bool? scrennShareEnabled}) async {
  if (_currentRoomInfo != null) {
    Logger.error("already in room");
    return;
  }
  final enableScreenShare = screenShareEnabled ?? scrennShareEnabled ?? false;
  final connectStopwatch = Stopwatch()..start();
  Logger.info(
    '[Room] joinRoom start room=${roomInfo.roomName} url=${roomInfo.url} '
    'loginUID=${roomInfo.loginUID} rtcType=${roomInfo.rtcType} '
    'uids=${roomInfo.uidList} mic=$micEnabled camera=$cameraEnabled '
    'screen=$enableScreenShare timeout=${roomInfo.timeout}',
  );
  _currentRoomInfo = roomInfo;
  _setConnectStatus(
      roomInfo.roomName, ConnectStatus.connecting, "connecting");

  room = Room(
    roomOptions: RoomOptions(
      // AdaptiveStream: 根据视频元素尺寸自动调整订阅质量
      adaptiveStream: true,
      // Dynacast: 动态暂停没有订阅者的视频层,节省带宽
      dynacast: true,
      // 视频捕获默认设置(发布端)- 4K 最高画质
      defaultCameraCaptureOptions: const CameraCaptureOptions(
        maxFrameRate: 30,
        params: VideoParametersPresets.h2160_169, // 4K: 3840x2160
      ),
      // 发布默认设置
      defaultVideoPublishOptions: VideoPublishOptions(
        simulcast: true, // 开启 simulcast 多层级发布
        videoCodec: 'vp8', // 编码器
        // 主层级的编码参数(4K 最高质量)
        videoEncoding: const VideoEncoding(
          maxBitrate: 8000000, // 8Mbps (4K)
          maxFramerate: 30,
        ),
        // 自定义 simulcast 层级(从低到高,网络差时自动降级)
        videoSimulcastLayers: [
          VideoParametersPresets.h360_169, // 640x360, 450kbps
          VideoParametersPresets.h720_169, // 1280x720, 1.7Mbps
          VideoParametersPresets.h1080_169, // 1920x1080, 3Mbps
        ],
      ),
    ),
  );
  listener = room!.createListener();
  listener!
    ..on<RoomDisconnectedEvent>((event) {
      Logger.info(
        '[RoomEvent] disconnected room=${roomInfo.roomName} '
        'reason=${event.reason} remoteParticipants=${room?.remoteParticipants.length}',
      );
      // disconnect
      _setConnectStatus(
          roomInfo.roomName, ConnectStatus.disconnected, "disconnected");
      TgoRTC.instance.participantManager.getLocalParticipant()?.notifyLeave();
    })
    ..on<RoomAttemptReconnectEvent>((event) {
      Logger.info('[RoomEvent] attemptReconnect room=${roomInfo.roomName}');
      // reconnect
      _setConnectStatus(
          roomInfo.roomName, ConnectStatus.reconnecting, "reconnecting");
    })
    ..on<RoomConnectedEvent>((event) {
      Logger.info(
        '[RoomEvent] connected room=${roomInfo.roomName} '
        'local=${room?.localParticipant?.identity} '
        'remoteParticipants=${room?.remoteParticipants.length} '
        'elapsedMs=${connectStopwatch.elapsedMilliseconds}',
      );
      // connected
      _setConnectStatus(
          roomInfo.roomName, ConnectStatus.connected, "connected");
      TgoRTC.instance.participantManager
          .getLocalParticipant()
          ?.setLocalParticipant(room!.localParticipant!);
      TgoRTC.instance.participantManager.getLocalParticipant()?.notifyJoined();
      for (final participant in room!.remoteParticipants.values) {
        TgoRTC.instance.participantManager.setParticipantJoin(participant);
      }
    })
    ..on<RoomReconnectingEvent>((event) {
      Logger.info('[RoomEvent] reconnecting room=${roomInfo.roomName}');
      // reconnecting
      _setConnectStatus(
          roomInfo.roomName, ConnectStatus.reconnecting, "reconnecting");
    })
    ..on<RoomReconnectedEvent>((event) {
      Logger.info(
        '[RoomEvent] reconnected room=${roomInfo.roomName} '
        'local=${room?.localParticipant?.identity} '
        'remoteParticipants=${room?.remoteParticipants.length}',
      );
      _setConnectStatus(
          roomInfo.roomName, ConnectStatus.reconnected, "reconnected");
      TgoRTC.instance.participantManager
          .getLocalParticipant()
          ?.setLocalParticipant(room!.localParticipant!);
      TgoRTC.instance.participantManager.getLocalParticipant()?.notifyJoined();
    })
    ..on<ParticipantConnectedEvent>((event) {
      Logger.info(
        '[RoomEvent] participantConnected uid=${event.participant.identity} '
        'sid=${event.participant.sid} '
        'connectionQuality=${event.participant.connectionQuality} '
        'remoteCount=${room?.remoteParticipants.length}',
      );
      // remote join
      TgoRTC.instance.participantManager
          .setParticipantJoin(event.participant);
    })
    ..on<ParticipantDisconnectedEvent>((event) {
      Logger.info(
        '[RoomEvent] participantDisconnected uid=${event.participant.identity} '
        'sid=${event.participant.sid} '
        'remoteCount=${room?.remoteParticipants.length}',
      );
      // remote leave
      TgoRTC.instance.participantManager
          .setParticipantLeave(event.participant);
    })
    ..on<LocalTrackPublishedEvent>((event) {
      Logger.info(
        '[RoomEvent] localTrackPublished kind=${event.publication.kind} '
        'source=${event.publication.source} sid=${event.publication.sid} '
        'track=${event.publication.track?.sid}',
      );
      // 调试日志:打印本地发布的视频轨道信息
      final publication = event.publication;
      if (publication.kind == TrackType.VIDEO) {
        final track = publication.track;
        if (track is LocalVideoTrack) {
          final options = track.currentOptions;
          final dimensions = options.params.dimensions;
          Logger.info(
              '[Video] Local track published: ${publication.sid}, '
              'resolution: ${dimensions.width}x${dimensions.height}');

          // 订阅视频统计信息事件
          _subscribeToVideoStats(track);
        }
      }
    })
    ..on<LocalTrackUnpublishedEvent>((event) {
      Logger.info(
        '[RoomEvent] localTrackUnpublished kind=${event.publication.kind} '
        'source=${event.publication.source} sid=${event.publication.sid}',
      );
      // 本地轨道取消发布时清理
      if (event.publication.kind == TrackType.VIDEO) {
        _unsubscribeFromVideoStats();
      }
    });
  Logger.info(
    '[Room] connect begin room=${roomInfo.roomName} '
    'url=${roomInfo.url} tokenLength=${roomInfo.token.length}',
  );
  try {
    await room!.connect(
      roomInfo.url,
      roomInfo.token,
    );
    Logger.info(
      '[Room] connect success room=${roomInfo.roomName} '
      'local=${room?.localParticipant?.identity} '
      'remoteParticipants=${room?.remoteParticipants.length} '
      'elapsedMs=${connectStopwatch.elapsedMilliseconds}',
    );
  } catch (e, s) {
    Logger.error(
      '[Room] connect failed room=${roomInfo.roomName} '
      'url=${roomInfo.url} elapsedMs=${connectStopwatch.elapsedMilliseconds} '
      'errorType=${e.runtimeType} error=$e',
    );
    Logger.error('[Room] connect failed stack=$s');
    rethrow;
  } finally {
    connectStopwatch.stop();
  }

  if (cameraEnabled) {
    Logger.info('[Room] enable camera begin room=${roomInfo.roomName}');
    await room!.localParticipant?.setCameraEnabled(true);
    Logger.info('[Room] enable camera done room=${roomInfo.roomName}');
  }
  if (cameraEnabled || enableScreenShare) {
    Logger.info(
      '[Room] wait after video publish room=${roomInfo.roomName} '
      'camera=$cameraEnabled screen=$enableScreenShare',
    );
    await Future.delayed(const Duration(milliseconds: 300));
  }
  if (micEnabled) {
    Logger.info('[Room] enable microphone begin room=${roomInfo.roomName}');
    await room!.localParticipant?.setMicrophoneEnabled(true);
    Logger.info('[Room] enable microphone done room=${roomInfo.roomName}');
  }
  if (enableScreenShare) {
    Logger.info('[Room] enable screen share begin room=${roomInfo.roomName}');
    await Future.delayed(const Duration(milliseconds: 300));
    await room!.localParticipant?.setScreenShareEnabled(true);
    Logger.info('[Room] enable screen share done room=${roomInfo.roomName}');
  }

  // 开启定时器检查参与者超时
  Logger.info(
    '[Room] start timeout checker room=${roomInfo.roomName} '
    'timeout=${roomInfo.timeout}',
  );
  _startTimeoutChecker(roomInfo.timeout);
}