Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intermittent issues in conference calls #359

Open
waleedqaefi opened this issue Apr 30, 2024 · 1 comment
Open

Intermittent issues in conference calls #359

waleedqaefi opened this issue Apr 30, 2024 · 1 comment

Comments

@waleedqaefi
Copy link

waleedqaefi commented Apr 30, 2024

Specify the sample to which the issue belongs (use [x]):
[] Conference Calls sample

Platform (use [x])
[] Android
[] iOS

Describe the bug:

We are currently getting intermittent issues in some calls , both primary and minor streams shows blank white screen. I am adding our conversation_call_screen code . Can you please have a look if there is something wrong in our implementation?

Additional info

import 'dart:async';
import 'dart:io';

import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'package:customer_io/customer_io.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wakelock/wakelock.dart';

import '../../../../config/video_call_managers/call_manager.dart';
import '../../../../config/video_call_utils/connectycube_local_storage_manager.dart';
import '../../../../config/video_call_utils/consts.dart';
import '../../../../config/video_call_utils/duration_timer.dart';
import '../../../../config/video_call_utils/media_utils.dart';
import '../../../../config/video_call_utils/platform_utils.dart';
import '../../../../config/video_call_utils/waiting_timer.dart';
import '../../../../core/app/video_call_navigation_manager.dart';
import '../../../../core/general/services/service_locator.dart';
import '../../../../core/general/theme/app_colors.dart';
import '../../../chat_v2/presentation/bloc/chat_v2_bloc.dart';
import '../../../find_for_me/domain/entities/find_for_me_request_entity.dart';
import '../../../find_for_me/domain/params/change_find_for_me_request_params.dart';
import '../../../find_for_me/presentation/bloc/find_for_me_bloc.dart';
import '../../domain/entities/call_request_entity.dart';
import '../../domain/params/conversation_call_view_params.dart';
import '../../domain/params/save_session_params.dart';
import '../bloc/video_call_bloc.dart';
import '../widgets/call_controls_widget.dart';
import '../widgets/hide_call_view_button.dart';
import '../widgets/notifications/taaly_bot_notification/taaly_bot_notifications.dart';
import '../widgets/private_call_widget.dart';
import '../widgets/video_call_camera_off_view.dart';
import '../widgets/video_call_mini_bar.dart';

class ConversationCallView extends StatefulWidget {
  static const String routeName = "call_conversation_view";
  static const String path = "/call_conversation_view";

  final ConversationsCallViewParams conversationParams;

  const ConversationCallView({super.key, required this.conversationParams});

  @override
  State<StatefulWidget> createState() {
    return _ConversationCallViewState();
  }

  static Widget addBlocProviderAndBuild(
    ConversationsCallViewParams conversationParams,
  ) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => sl<ChatV2Bloc>(),
        ),
        BlocProvider(
          create: (context) => sl<FindForMeBloc>(),
        ),
      ],
      child: ConversationCallView(conversationParams: conversationParams),
    );
  }
}

class _ConversationCallViewState extends State<ConversationCallView> {
  static const String tag = "_ConversationCallScreenState";
  final CallManager _callManager = CallManager.instance;
  late StreamSubscription<int> timerStream;
  late DurationTimer _callTimer;
  late CubeUser _currentUser;
  late ConferenceSession _callSession;
  late String _callName;
  late bool _isIncoming;
  late String _meetingId;
  late List<int> _opponents;
  late int currentUserId;
  final _statsReportsManager = CubeStatsReportsManager();
  MediaStream? initialLocalMediaStream;

  String _callStatus = 'Waiting...';
  bool _isCameraEnabled = true;
  bool _isSpeakerEnabled = true;
  bool _isMicMute = false;
  bool _isFrontCameraUsed = true;
  RTCVideoViewObjectFit primaryVideoFit =
      RTCVideoViewObjectFit.RTCVideoViewObjectFitCover;
  MapEntry<int, RTCVideoRenderer>? primaryRenderer;
  Map<int, RTCVideoRenderer> minorRenderers = {};
  Map<int, Map<String, bool>> participantsMediaConfigs = {};
  WidgetPosition _minorWidgetPosition = WidgetPosition.bottomRight;

  DateTime? startDate;
  DateTime? meetingStartDate;
  String? bookingId;
  String? findForMeRequestId;

  _setUpCallManagerMethods() {
    _callManager.onCallRejected = _onCallRejected;
    _callManager.onReceiveRejectCall = _onReceiveRejectCall;
    _callManager.onReceiveAcceptCall = _onReceiveAcceptCall;
    _callManager.onCloseCall = _onCloseCall;
    _callManager.onCallMuted = _onCallMuted;
    _callManager.getMediaState = _getMediaState;
    _callManager.onParticipantMediaUpdated = _onParticipantMediaUpdated;

    _callSession.onLocalStreamReceived = _addLocalMediaStream;
    _callSession.onRemoteStreamTrackReceived = _addRemoteMediaStream;
    _callSession.onSessionClosed = _onSessionClosed;
    _callSession.onPublishersReceived = onPublishersReceived;
    _callSession.onPublisherLeft = onPublisherLeft;
    _callSession.onError = onError;
    _callSession.onSubStreamChanged = onSubStreamChanged;
    _callSession.onLayerChanged = onLayerChanged;
  }

  void _initParams() {
    _currentUser = widget.conversationParams.currentUser;
    _callSession = widget.conversationParams.callSession;
    _callName = widget.conversationParams.callName;
    _isIncoming = widget.conversationParams.isIncoming;
    _meetingId = widget.conversationParams.meetingId;
    _opponents = widget.conversationParams.opponents;
    currentUserId = _currentUser.id!;
    _isCameraEnabled = _callSession.callType == CallType.VIDEO_CALL;
    findForMeRequestId = widget.conversationParams.findForMeRequestId;
    _opponentName = widget.conversationParams.opponentName ?? "";
    _opponentImage = widget.conversationParams.opponentImageUrl;
  }

  late String _opponentName;
  String? _opponentImage;

  @override
  void initState() {
    super.initState();
    CustomerIO.screen(name: ConversationCallView.routeName);
    if (Platform.isIOS) {
      Helper.setAppleAudioConfiguration(
        AppleAudioConfiguration(
          appleAudioMode: AppleAudioMode.videoChat,
        ),
      );
    }
    Wakelock.enable();

    _initParams();
    _statsReportsManager.init(_callSession);

    _callTimer = DurationTimer(
      onZeroSecond: () {},
    );

    if (!_isIncoming) {
      waitingTimer.start();
    }

    if (widget.conversationParams.isIncoming == true) {
      _startCallTimer();
      startDate = DateTime.now();
    }
    meetingStartDate = widget.conversationParams.startDate;
    bookingId = widget.conversationParams.bookingId;

    timerStream = _callTimer.durationStream.listen((second) {
      if (second == 0) {
        // IF INCOMING IS FALSE, THIS MEAN HE IS THE CALLER
        if (widget.conversationParams.isIncoming == false) _endCall();
      }
    });

    if (initialLocalMediaStream != null) {
      _isMicMute =
          (initialLocalMediaStream?.getAudioTracks().firstOrNull?.enabled ??
              false);

      _isCameraEnabled =
          initialLocalMediaStream?.getVideoTracks().firstOrNull?.enabled ??
              false;
    }

    participantsMediaConfigs[currentUserId] = {
      PARAM_IS_MIC_ENABLED: !_isMicMute,
      PARAM_IS_CAMERA_ENABLED: _isCameraEnabled
    };
    _setUpCallManagerMethods();

    context.read<VideoCallBloc>().add(GetVideoChatsEvent(
          meetingId: _meetingId,
        ));

    if (initialLocalMediaStream != null) {
      _callSession.localStream = initialLocalMediaStream;
      _addLocalMediaStream(initialLocalMediaStream!);
    }

    // TO FIX MIC MUTING INITIAL STATE
    _callSession.setMicrophoneMute(_isMicMute);

    _callSession.joinDialog(
      _meetingId,
      ((publishers) {
        log("join session= $publishers", tag);

        _callManager.requestParticipantsMediaConfig(publishers);

        _callSession.setMaxBandwidth(0);

        if (!_isIncoming) {
          _callManager.startNewOutgoingCall(
            _meetingId,
            _opponents,
            _callSession.currentUserId,
            _callSession.callType,
            _callName,
            _currentUser.avatar,
            widget.conversationParams.startDate,
          );
        } else {
          setState(() {
            _callStatus = 'Connected';
            _startCallTimer();
          });
        }
      }),
      conferenceRole: ConferenceRole.PUBLISHER,
    );
  }

  @override
  void dispose() {
    _statsReportsManager.dispose();
    Wakelock.disable();

    stopBackgroundExecution();

    minorRenderers.forEach((opponentId, renderer) {
      log("[dispose] dispose renderer for $opponentId", tag);
      try {
        renderer.srcObject = null;
        renderer.dispose();
      } catch (e) {
        log('Error On [minorRenderers.forEach] $e');
      }
    });
    //! NEW ADDS TO CLEAR VIDEO CALL
    minorRenderers.clear();
    primaryRenderer?.value.dispose();
    //! END HERE
    _stopCallTimer();

    super.dispose();
  }

  @override
  deactivate() {
    context.read<VideoCallBloc>().cancelAllSubscriptions();
    super.deactivate();
  }

  Future<void> viewRatingBottomSheet() async {
    if (startDate == null) return;
    if (isMinimized == true) {
      setState(() {
        isMinimized = false;
        isWasMinimized = true;
      });
    }
    int counter = 0;
    while (VideoCallNavigationManager.navigatorKey.currentContext == null &&
        counter < 15) {
      await Future.delayed(const Duration(milliseconds: 300));
      counter++;
    }
    if (VideoCallNavigationManager.navigatorKey.currentContext == null) return;
    await context.read<VideoCallBloc>().showRatingDialog(
          _meetingId,
          VideoCallNavigationManager.navigatorKey.currentContext!,
        );
  }

  bool isMinimized = false;
  bool isWasMinimized = false;
  final WaitingTimer waitingTimer = WaitingTimer();

  @override
  Widget build(BuildContext context) {
    VideoCallNavigationManager.instance.context = context;
    return WillPopScope(
      onWillPop: () => _onBackPressed(context),
      child: BlocListener<VideoCallBloc, VideoCallState>(
        listener: (context, state) {
          if (state is UpdateChatSuccess && meetingStartDate == null) {
            if (widget.conversationParams.isIncoming == false) return;
            meetingStartDate = state.chat.meetingStartDate;
            if (meetingStartDate == null) return;
            _callTimer.modifyDurationSecBasedOnStartDate(meetingStartDate!);
          }
          if (state is UpdateChatSuccess && bookingId == null) {
            if (widget.conversationParams.isIncoming == false) return;
            bookingId = state.chat.bookingId;
            findForMeRequestId = state.chat.findForMeRequestId;
            if (_isIncoming) {
              _saveSessionData();
            }
          }
        },
        child: !isMinimized
            ? MaterialApp(
                navigatorKey: VideoCallNavigationManager.navigatorKey,
                debugShowCheckedModeBanner: false,
                home: Scaffold(
                  backgroundColor: AppColors.deactivatedColor,
                  body: !isWasMinimized
                      ? Stack(
                          children: [
                            _buildBody(),
                            HideCallViewBtn(
                              onPressed: () {
                                setState(() => isMinimized = true);
                              },
                            ),
                          ],
                        )
                      : VideoCallCameraOffView(
                          opponentName: _opponentName,
                          opponentImage: _opponentImage,
                          timerStream: _callTimer.durationStream,
                        ),
                ),
              )
            : VideoCallMiniBar(
                timerStream: _callTimer.durationSec != 30 * 60
                    ? _callTimer.durationStream
                    : waitingTimer.durationStream,
                opponentName: _opponentName,
                onPressed: () {
                  setState(() => isMinimized = false);
                },
                isInWaiting: _callTimer.durationSec == 30 * 60,
              ),
      ),
    );
  }

  Widget _buildBody() {
    return Stack(
      children: [
        OrientationBuilder(
          builder: (context, orientation) => _buildPrivateCallLayout(
            orientation,
          ),
        ),
        Align(
          alignment: Alignment.bottomCenter,
          child: _getActionsPanel(),
        ),
        const TaalyBotNotifications(
          addTopMarginForFirstMessage: true,
        ),
      ],
    );
  }

  Widget _buildPrivateCallLayout(Orientation orientation) {
    return PrivateCallLayout(
      currentUserId: currentUserId,
      primaryRenderer: primaryRenderer,
      primaryVideoFit: primaryVideoFit,
      minorRenderers: minorRenderers,
      callName: _getCallName(),
      callStatus: _callStatus,
      callTimer: _callTimer,
      minorWidgetInitialPosition: _minorWidgetPosition,
      isFrontCameraUsed: _isFrontCameraUsed,
      participantsMediaConfigs: participantsMediaConfigs,
      onMinorVideoPositionChanged: (newPosition) {
        _minorWidgetPosition = newPosition;
      },
      onPrimaryVideoFitChanged: (newObjectFit) {
        primaryVideoFit = newObjectFit;
      },
      onRenderersChanged: _updateRenderers,
      opponentImageUrl: _opponentImage,
      opponentName: _opponentName,
      viewWaitingView: _callTimer.durationSec == 30 * 60,
      waitingTimer: waitingTimer,
    );
  }

  Widget _getActionsPanel() {
    return CallControls(
      isMicMuted: _isMicMute,
      onMute: _muteMic,
      isCameraButtonVisible: true,
      isCameraEnabled: _isVideoEnabled(),
      onToggleCamera: _toggleCamera,
      isSpeakerEnabled: _isSpeakerEnabled,
      onSwitchSpeaker: _switchSpeaker,
      isSwitchCameraButtonVisible: true,
      onSwitchCamera: _switchCamera,
      onEndCall: _endCall,
      meetingId: widget.conversationParams.meetingId,
    );
  }

  Future<bool> _onBackPressed(BuildContext context) {
    return Future.value(false);
  }

  void _onCloseCall() async {
    log("_onCloseCall", tag);
    if (startDate != null) {
      _stopCallTimer();
    }
    _callSession.leave();
  }

  void _onReceiveRejectCall(String meetingId, int participantId, bool isBusy) {
    log("_onReceiveRejectCall got reject from user $participantId", tag);
    final callRequestId = widget.conversationParams.callRequestId;
    if (callRequestId != null) {
      context.read<VideoCallBloc>().add(
            UpdateCallRequestEvent(
              requestId: widget.conversationParams.callRequestId!,
              callRequestStatus: CallRequestStatus.rejected,
            ),
          );
    }

    VideoCallNavigationManager.instance.disposeVideoCall(
      popPreviousRoute: false,
    );
  }

  void _changeStatus({String? studentId, String? tutorId}) {
    context.read<FindForMeBloc>().add(
          ChangeFindForMeRequestEvent(
            changeFindForMeRequestParams: ChangeFindForMeRequestParams(
              requestId: widget.conversationParams.findForMeRequestId!,
              status: FindMeRequestStatus.accepted,
              studentId: studentId,
              tutorId: tutorId,
            ),
          ),
        );
  }

  void _onReceiveAcceptCall(int participantId) {
    log('[_onReceiveAcceptCall] from user $participantId', tag);
    if (!_isIncoming) _saveSessionData();

    final videoCallBloc = context.read<VideoCallBloc>();

    final callRequestId = widget.conversationParams.callRequestId;
    if (callRequestId != null) {
      videoCallBloc.add(
        UpdateCallRequestEvent(
          requestId: widget.conversationParams.callRequestId!,
          callRequestStatus: CallRequestStatus.accepted,
        ),
      );
    }

    videoCallBloc.add(CreateNewCallChatEvent(
      meetingId: widget.conversationParams.meetingId,
      meetingStartDate: widget.conversationParams.startDate,
      bookingId: bookingId,
      findForMeRequestId: findForMeRequestId,
    ));

    if (widget.conversationParams.findForMeRequestId != null) {
      _changeStatus(
        studentId: videoCallBloc.studentId,
        tutorId: videoCallBloc.tutorId,
      );
    }

    setState(() {
      _callStatus = 'Connected';
      _startCallTimer();
      startDate = DateTime.now();
    });
  }

  _saveSessionData() async {
    if (bookingId != null) return;
    final videoCallBloc = context.read<VideoCallBloc>();

    await ConnectycubeLocalStorageManager.saveLastSessionData(
      tutorId: videoCallBloc.tutorId!,
      studentId: videoCallBloc.studentId!,
      meetingId: _meetingId,
      bookingId: bookingId,
      findForMeRequestId: findForMeRequestId,
    );
  }

  Future<void> _addLocalMediaStream(MediaStream stream) async {
    log("_addLocalMediaStream", tag);

    _addMediaStream(currentUserId, true, stream);
  }

  void _addRemoteMediaStream(session, int userId, MediaStream stream,
      {String? trackId}) {
    log("_addRemoteMediaStream for user $userId", tag);

    _addMediaStream(userId, false, stream, trackId: trackId);
  }

  void _removeMediaStream(callSession, int userId) {
    log("_removeMediaStream for user $userId", tag);
    RTCVideoRenderer? videoRenderer = minorRenderers[userId];
    if (videoRenderer != null) {
      videoRenderer.srcObject = null;
      videoRenderer.dispose();

      setState(() {
        minorRenderers.remove(userId);
      });
    } else if (primaryRenderer?.key == userId) {
      var rendererToRemove = primaryRenderer?.value;

      if (rendererToRemove != null) {
        rendererToRemove.srcObject = null;
        rendererToRemove.dispose();
      }

      if (minorRenderers.isNotEmpty) {
        setState(() {
          var userIdToRemoveRenderer = minorRenderers.keys.firstWhere(
              (key) => key != currentUserId,
              orElse: () => minorRenderers.keys.first);

          primaryRenderer = MapEntry(userIdToRemoveRenderer,
              minorRenderers.remove(userIdToRemoveRenderer)!);
          chooseOpponentsStreamsQuality(_callSession, currentUserId,
              {userIdToRemoveRenderer: StreamType.high});
        });
      }
    }
  }

  void _closeSessionIfLast() {
    log("[_closeSessionIfLast]", tag);
    if (_callSession.allActivePublishers.isEmpty) {
      log("[_closeSessionIfLast] 1", tag);
      _callSession.leave();
    }
  }

  void _onSessionClosed(session) async {
    log("[_onSessionClosed]", tag);
    if (startDate != null && !_isIncoming) {
      await _sendDataToCloud();
    }
    _statsReportsManager.dispose();
    _callManager.stopCall(_currentUser);

    try {
      await viewRatingBottomSheet();
    } catch (e) {
      log(e.toString());
    }

    if (startDate != null && _isIncoming) {
      await _sendDataToCloud();
    }

    log("[_onSessionClosed] 2", tag);

    if (!context.mounted) return;

    VideoCallNavigationManager.instance.disposeVideoCall(
      popPreviousRoute: true,
    );
  }

  void onPublishersReceived(publishers) {
    log("onPublishersReceived", tag);
    handlePublisherReceived(publishers);
  }

  void onPublisherLeft(publisher) {
    log("onPublisherLeft $publisher", tag);
    _removeMediaStream(_callSession, publisher);
    _closeSessionIfLast();
  }

  void onError(ex) => log("onError $ex", tag);

  void onSubStreamChanged(int userId, StreamType streamType) {
    log("onSubStreamChanged userId: $userId, streamType: $streamType", tag);
  }

  void onLayerChanged(int userId, int layer) {
    log("onLayerChanged userId: $userId, layer: $layer", tag);
  }

  Future<void> _addMediaStream(
      int userId, bool isLocalStream, MediaStream stream,
      {String? trackId}) async {
    log('[_addMediaStream] userId: $userId, isLocalStream: $isLocalStream',
        tag);

    if (primaryRenderer == null) {
      primaryRenderer = MapEntry(userId, RTCVideoRenderer());
      await primaryRenderer!.value.initialize();

      setState(() {
        _setSourceForRenderer(primaryRenderer!.value, stream, isLocalStream,
            trackId: trackId);

        chooseOpponentsStreamsQuality(_callSession, currentUserId, {
          userId: StreamType.high,
        });
      });

      return;
    }

    if (primaryRenderer?.key == userId) {
      _setSourceForRenderer(primaryRenderer!.value, stream, isLocalStream,
          trackId: trackId);

      chooseOpponentsStreamsQuality(_callSession, currentUserId, {
        userId: StreamType.high,
      });

      return;
    }

    if (minorRenderers[userId] == null) {
      minorRenderers[userId] = RTCVideoRenderer();
      await minorRenderers[userId]?.initialize();
    }

    setState(() {
      _setSourceForRenderer(minorRenderers[userId]!, stream, isLocalStream,
          trackId: trackId);

      if (primaryRenderer?.key == currentUserId ||
          primaryRenderer?.key == userId ||
          ((primaryRenderer?.value.srcObject?.getVideoTracks().isEmpty ??
                  false) &&
              stream.getVideoTracks().isNotEmpty)) {
        _updatePrimaryUser(userId, true);
      }
    });
  }

  _setSourceForRenderer(
      RTCVideoRenderer renderer, MediaStream stream, bool isLocalStream,
      {String? trackId}) {
    isLocalStream || kIsWeb
        ? renderer.srcObject = stream
        : renderer.setSrcObject(stream: stream, trackId: trackId);
  }

  void handlePublisherReceived(List<int?> publishers) {
    if (!_isIncoming) {
      for (var id in publishers) {
        if (id != null) {
          _callManager.handleAcceptCall(id);
        }
      }
    }

    if (publishers.isNotEmpty) {
      _callManager.requestParticipantsMediaConfig(publishers);
    }
  }

  void _updatePrimaryUser(int userId, bool force) {
    log("[_updatePrimaryUser] userId: $userId, force: $force", tag);

    log("[_updatePrimaryUser] 2", tag);
    updatePrimaryUser(
      userId,
      force,
      currentUserId,
      primaryRenderer,
      minorRenderers,
      participantsMediaConfigs,
      onRenderersUpdated: _updateRenderers,
    );
    log("[_updatePrimaryUser] 3", tag);
  }

  _updateRenderers(MapEntry<int, RTCVideoRenderer>? updatedPrimaryRenderer,
      Map<int, RTCVideoRenderer> updatedMinorRenderers) {
    if (updatedPrimaryRenderer?.key != primaryRenderer?.key) {
      chooseOpponentsStreamsQuality(_callSession, currentUserId, {
        if (updatedPrimaryRenderer?.key != null)
          updatedPrimaryRenderer!.key: StreamType.high,
        if (primaryRenderer?.key != null) primaryRenderer!.key: StreamType.low,
      });
    }

    primaryRenderer = updatedPrimaryRenderer;
    minorRenderers = updatedMinorRenderers;
  }

  _endCall() {
    _callManager.stopCall(_currentUser);
    _callSession.leave();
  }

  Future<void> _sendDataToCloud() async {
    final videoCallBloc = context.read<VideoCallBloc>();

    if (videoCallBloc.tutorId == null || videoCallBloc.studentId == null) {
      return;
    }

    context.read<VideoCallBloc>().add(
          SaveSessionToCloudEvent(
            saveSessionParams: SaveSessionParams(
              tutorId: videoCallBloc.tutorId!,
              studentId: videoCallBloc.studentId!,
              startDate: startDate!,
              meetingId: _meetingId,
              endDate: DateTime.now(),
              bookingId: bookingId,
              findForMeRequestId: findForMeRequestId,
              isIncoming: _isIncoming,
            ),
          ),
        );
  }

  _muteMic() {
    setState(() {
      _isMicMute = !_isMicMute;
      _callSession.setMicrophoneMute(_isMicMute);
      _callManager.muteMic(_meetingId, _isMicMute);
      notifyParticipantsMediaUpdated();
    });
  }

  _switchCamera() {
    if (!_isVideoEnabled()) return;
    _callSession.switchCamera().then((isFrontCameraUsed) {
      setState(() {
        _isFrontCameraUsed = isFrontCameraUsed;
      });
    });
  }

  _toggleCamera() {
    setState(() {
      _isCameraEnabled = !_isCameraEnabled;
      _callSession.setVideoEnabled(_isCameraEnabled);
      notifyParticipantsMediaUpdated();
    });
  }

  bool _isVideoEnabled() => _isVideoCall() && _isCameraEnabled;

  bool _isVideoCall() => CallType.VIDEO_CALL == _callSession.callType;

  _switchSpeaker() {
    setState(() {
      _isSpeakerEnabled = !_isSpeakerEnabled;
      _callSession.enableSpeakerphone(_isSpeakerEnabled);
    });
  }

  String _getCallName() => _callName;

  _startCallTimer() {
    return _callTimer.start(widget.conversationParams.startDate);
  }

  _stopCallTimer() => _callTimer.stop();

  void _onCallMuted(String meetingId, bool isMuted) {
    if (meetingId == _meetingId) {
      setState(() {
        _isMicMute = isMuted;
        _callSession.setMicrophoneMute(isMuted);
      });
    }
  }

  Map<String, bool> _getMediaState() => {
        PARAM_IS_MIC_ENABLED: !_isMicMute,
        PARAM_IS_CAMERA_ENABLED: _isCameraEnabled
      };

  void _onParticipantMediaUpdated(int userId, Map<String, bool> mediaConfig) {
    setState(() => participantsMediaConfigs[userId] = mediaConfig);
  }

  void notifyParticipantsMediaUpdated() {
    participantsMediaConfigs[currentUserId] = {
      PARAM_IS_MIC_ENABLED: !_isMicMute,
      PARAM_IS_CAMERA_ENABLED: _isCameraEnabled
    };

    _callManager.notifyParticipantsMediaUpdated({
      PARAM_IS_MIC_ENABLED: !_isMicMute,
      PARAM_IS_CAMERA_ENABLED: _isCameraEnabled
    });
  }

  void _onCallRejected(String meetingId) {
    final callRequestId = widget.conversationParams.callRequestId;
    if (callRequestId != null) {
      context.read<VideoCallBloc>().add(
            UpdateCallRequestEvent(
              requestId: widget.conversationParams.callRequestId!,
              callRequestStatus: CallRequestStatus.rejected,
            ),
          );
    }
    VideoCallNavigationManager.instance.disposeVideoCall(
      popPreviousRoute: false,
    );
  }
}

@TatankaConCube
Copy link
Contributor

Yesterday we had some updates related to the conference calls on our server. Could you try to reproduce your issue today? Can you reproduce your issue in our sample? Do you have similar issues there?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants