consumerResume function

Future<void> consumerResume(
  1. ConsumerResumeOptions options
)

Handles the resumption of a media stream (either audio or video) by managing the socket connections, updating the UI, and reordering streams as necessary.

This function performs comprehensive handling of media streams by leveraging socket connections, organizing UI components, and performing real-time updates to the video and audio grids.

  • Audio Resumption: Adds the resumed audio to the list of active audio streams, updates the UI with an audio-only component, and triggers a reordering of streams if required.
  • Video Resumption: Adds the resumed video to the list of active video streams, manages screen sharing updates (if applicable), and reorders streams based on the screen sharing state or participant role.

Parameters:

  • options (ConsumerResumeOptions): Contains the details of the stream to be resumed:
    • stream (MediaStream): The media stream that is being resumed.
    • kind (String): The type of the media stream, either 'audio' or 'video'.
    • remoteProducerId (String): The producer ID of the remote media stream.
    • parameters (ConsumerResumeParameters): Parameters for managing state updates and triggering functions.
    • nsock (io.Socket): The socket connection for managing real-time events.
    • consumer (Consumer): The consumer object associated with the media stream.

Example Usage:

await consumerResume(ConsumerResumeOptions(
  stream: myMediaStream,
  kind: 'audio',
  remoteProducerId: 'producerId123',
  parameters: consumerResumeParams,
  nsock: socket,
  consumer: myConsumer,
));

Returns:

  • A Future<void> that completes when the handling of the resumed media stream is finished.

Implementation

Future<void> consumerResume(ConsumerResumeOptions options) async {
  try {
    // Destructure options for easy access
    final MediaStream stream = options.stream;
    final String kind = options.kind;
    final String remoteProducerId = options.remoteProducerId;
    final ConsumerResumeParameters parameters = options.parameters;
    final io.Socket nsock = options.nsock;
    final Consumer consumer = options.consumer;

    // Refresh parameters using the latest state
    final ConsumerResumeParameters updatedParams =
        parameters.getUpdatedAllParams();

    // Destructure updatedParams using getters
    final MediaStream? nStream = updatedParams.nStream;
    List<Stream> allAudioStreams = updatedParams.allAudioStreams;
    List<Stream> allVideoStreams = updatedParams.allVideoStreams;
    final List<Stream> streamNames = updatedParams.streamNames;
    final List<Stream> audStreamNames = updatedParams.audStreamNames;
    final bool mainScreenFilled = updatedParams.mainScreenFilled;
    bool shareScreenStarted = updatedParams.shareScreenStarted;
    String screenId = updatedParams.screenId;
    final List<Participant> participants = updatedParams.participants;
    final EventType eventType = updatedParams.eventType;
    final String meetingDisplayType = updatedParams.meetingDisplayType;
    final bool lockScreen = updatedParams.lockScreen;
    final bool firstAll = updatedParams.firstAll;
    List<Stream> oldAllStreams = updatedParams.oldAllStreams;
    String adminVidID = updatedParams.adminVidID;
    double mainHeightWidth = updatedParams.mainHeightWidth;
    final String member = updatedParams.member;
    List<Widget> audioOnlyStreams = updatedParams.audioOnlyStreams;
    final bool gotAllVids = updatedParams.gotAllVids;
    final bool deferReceive = updatedParams.deferReceive;
    final bool firstRound = updatedParams.firstRound;
    List<Stream> remoteScreenStream = updatedParams.remoteScreenStream;
    final String hostLabel = updatedParams.hostLabel;
    final bool whiteboardStarted = updatedParams.whiteboardStarted;
    final bool whiteboardEnded = updatedParams.whiteboardEnded;

    // Update functions
    final Function(bool) updateUpdateMainWindow =
        updatedParams.updateUpdateMainWindow;
    final Function(List<Stream>) updateAllAudioStreams =
        updatedParams.updateAllAudioStreams;
    final Function(List<Stream>) updateAllVideoStreams =
        updatedParams.updateAllVideoStreams;
    final Function(List<Stream>) updateStreamNames =
        updatedParams.updateStreamNames;
    final Function(List<Stream>) updateAudStreamNames =
        updatedParams.updateAudStreamNames;
    final Function(MediaStream) updateNStream = updatedParams.updateNStream;
    final Function(double) updateMainHeightWidth =
        updatedParams.updateMainHeightWidth;
    final Function(bool) updateLockScreen = updatedParams.updateLockScreen;
    final Function(bool) updateFirstAll = updatedParams.updateFirstAll;
    final Function(List<Stream>) updateRemoteScreenStream =
        updatedParams.updateRemoteScreenStream;
    final Function(List<Stream>) updateOldAllStreams =
        updatedParams.updateOldAllStreams;
    final Function(List<Widget>) updateAudioOnlyStreams =
        updatedParams.updateAudioOnlyStreams;
    final Function(bool) updateShareScreenStarted =
        updatedParams.updateShareScreenStarted;
    final Function(bool) updateGotAllVids = updatedParams.updateGotAllVids;
    final Function(String) updateScreenId = updatedParams.updateScreenId;
    final Function(bool) updateDeferReceive = updatedParams.updateDeferReceive;

    // Mediasfu functions
    final ReorderStreamsType reorderStreams = updatedParams.reorderStreams;
    final PrepopulateUserMediaType prepopulateUserMedia =
        updatedParams.prepopulateUserMedia;

    if (kind == 'audio') {
      // ----- Handling Audio Resumption -----

      // Find participant with audioID == remoteProducerId
      Participant? participant;
      try {
        participant =
            participants.firstWhere((p) => p.audioID == remoteProducerId);
      } catch (e) {
        participant = null;
      }

      String name = participant?.name ?? '';

      // If the participant is the host, no action is needed
      if (name == hostLabel) {
        return;
      }

      // Find any participant currently sharing the screen
      Participant? screenParticipantAlt;
      try {
        screenParticipantAlt = participants.firstWhere(
          (p) =>
              p.ScreenID != null &&
              p.ScreenOn == true &&
              p.ScreenID!.isNotEmpty,
        );
      } catch (e) {
        screenParticipantAlt = null;
      }

      if (screenParticipantAlt != null) {
        screenId = screenParticipantAlt.ScreenID!;
        updateScreenId(screenId);
        if (!shareScreenStarted) {
          updateShareScreenStarted(true);
        }
      } else if (whiteboardStarted && !whiteboardEnded) {
        // Whiteboard is active; no changes to screen sharing
      } else {
        // No screen sharing; reset screen ID and share screen status
        screenId = "";
        updateScreenId(screenId);
        updateShareScreenStarted(false);
      }

      // Update the main media stream
      final nStream = stream;
      updateNStream(nStream);

      // Create a MiniAudioPlayer widget
      final optionsMiniAudio = MiniAudioPlayerOptions(
        stream: nStream,
        consumer: consumer,
        remoteProducerId: remoteProducerId,
        parameters: updatedParams,
        miniAudioComponent: (props) => MiniAudio(
            options: MiniAudioOptions(
          name: name,
          showWaveform: true,
        )),
        miniAudioProps: {
          'customStyle': const {
            'backgroundColor': Color.fromARGB(255, 23, 23, 23),
          },
          'name': name,
          'showWaveform': true,
          'overlayPosition': 'topRight',
          'barColor': Colors.white,
          'textColor': Colors.white,
          'imageSource': 'https://mediasfu.com/images/logo192.png',
          'roundedImage': true,
          'imageStyle': const {},
        },
      );
      final Widget nTrack = MiniAudioPlayer(
        options: optionsMiniAudio,
      );

      // Add the MiniAudioPlayer to audio-only streams
      audioOnlyStreams.add(nTrack);
      updateAudioOnlyStreams(audioOnlyStreams);

      // Add the new audio stream to allAudioStreams
      allAudioStreams
          .add(Stream(producerId: remoteProducerId, stream: nStream));
      updateAllAudioStreams(allAudioStreams);

      if (name.isNotEmpty) {
        // Add to audStreamNames
        final newAudioStream = Stream.fromMap({
          'producerId': remoteProducerId,
          'name': name,
        });

        audStreamNames.add(newAudioStream);
        updateAudStreamNames(audStreamNames);

        // If the main screen is not filled and the participant is at level 2, prepopulate user media
        if (!mainScreenFilled && participant!.islevel == '2') {
          updateUpdateMainWindow(true);
          var paramsPrepopulate = updatedParams;
          paramsPrepopulate.updateAllAudioStreams(allAudioStreams);
          paramsPrepopulate.updateAudStreamNames(audStreamNames);

          final optionsPrepopulate = PrepopulateUserMediaOptions(
            name: hostLabel,
            parameters: paramsPrepopulate,
          );
          await prepopulateUserMedia(
            optionsPrepopulate,
          );
          updateUpdateMainWindow(false);
        }
      } else {
        return;
      }

      // Determine display type and update UI accordingly
      bool checker;
      bool altChecker = false;

      if (meetingDisplayType == 'video') {
        checker =
            participant!.name.isNotEmpty && participant.videoID.isNotEmpty;
      } else {
        checker = true;
        altChecker = true;
      }

      if (checker) {
        if (shareScreenStarted) {
          if (!altChecker) {
            // Reorder streams based on updated parameters
            final optionsReorder = ReorderStreamsOptions(
              parameters: updatedParams,
            );
            await reorderStreams(
              optionsReorder,
            );

            if (!kIsWeb) {
              await Future.delayed(const Duration(milliseconds: 1000));
              await reorderStreams(
                optionsReorder,
              );
            }
          }
        } else {
          if (altChecker && meetingDisplayType != 'video') {
            final optionsReorder = ReorderStreamsOptions(
              add: false,
              screenChanged: true,
              parameters: updatedParams,
            );
            await reorderStreams(
              optionsReorder,
            );
            if (!kIsWeb) {
              await Future.delayed(const Duration(milliseconds: 1000));
              await reorderStreams(
                optionsReorder,
              );
            }
          }
        }
      }
    } else if (kind == 'video') {
      // ----- Handling Video Resumption -----

      // Update the main media stream
      final nStream = stream;
      updateNStream(nStream);

      // Find any participant currently sharing the screen
      Participant? screenParticipantAlt;
      try {
        screenParticipantAlt = participants.firstWhere(
          (p) =>
              p.ScreenID != null &&
              p.ScreenOn == true &&
              p.ScreenID!.isNotEmpty,
        );
      } catch (e) {
        screenParticipantAlt = null;
      }

      if (screenParticipantAlt != null) {
        screenId = screenParticipantAlt.ScreenID!;
        updateScreenId(screenId);
        if (!shareScreenStarted) {
          shareScreenStarted = true;
          updateShareScreenStarted(true);
        }
      } else if (whiteboardStarted && !whiteboardEnded) {
        // Whiteboard is active; no changes to screen sharing
      } else {
        // No screen sharing; reset screen ID and share screen status
        screenId = "";
        updateScreenId(screenId);
        updateShareScreenStarted(false);
      }

      // Check if the resumed video is a screen share
      if (remoteProducerId == screenId) {
        // Manage screen sharing on the main screen
        updateUpdateMainWindow(true);
        final newRemoteScreen = Stream(
            producerId: remoteProducerId, stream: nStream, socket_: nsock);

        remoteScreenStream = [
          newRemoteScreen,
        ];
        updateRemoteScreenStream(remoteScreenStream);

        if (eventType == EventType.conference) {
          if (shareScreenStarted) {
            if (mainHeightWidth == 0) {
              updateMainHeightWidth(84);
            }
          } else {
            if (mainHeightWidth > 0) {
              updateMainHeightWidth(0);
            }
          }
        }

        if (!lockScreen) {
          final optionsPrepopulate = PrepopulateUserMediaOptions(
            name: hostLabel,
            parameters: updatedParams,
          );
          await prepopulateUserMedia(
            optionsPrepopulate,
          );
          final optionsReorder = ReorderStreamsOptions(
            add: false,
            screenChanged: true,
            parameters: updatedParams,
          );
          await reorderStreams(
            optionsReorder,
          );

          if (!kIsWeb) {
            await Future.delayed(const Duration(milliseconds: 1000));
            final optionsPrepopulate = PrepopulateUserMediaOptions(
              name: hostLabel,
              parameters: updatedParams,
            );
            final optionsReorder = ReorderStreamsOptions(
              add: false,
              screenChanged: true,
              parameters: updatedParams,
            );
            await prepopulateUserMedia(
              optionsPrepopulate,
            );
            await reorderStreams(
              optionsReorder,
            );
          }
        } else {
          if (!firstAll) {
            final optionsPrepopulate = PrepopulateUserMediaOptions(
              name: hostLabel,
              parameters: updatedParams,
            );
            final optionsReorder = ReorderStreamsOptions(
              add: false,
              screenChanged: true,
              parameters: updatedParams,
            );
            await prepopulateUserMedia(
              optionsPrepopulate,
            );
            await reorderStreams(
              optionsReorder,
            );

            if (!kIsWeb) {
              await Future.delayed(const Duration(milliseconds: 1000));
              await prepopulateUserMedia(
                optionsPrepopulate,
              );
              await reorderStreams(
                optionsReorder,
              );
            }
          }
        }

        // Update lock screen and firstAll flags
        updateLockScreen(true);
        updateFirstAll(true);
      } else {
        // Non-screen share video resumed

        // Find the participant associated with the resumed video
        Participant? participant;
        try {
          participant =
              participants.firstWhere((p) => p.videoID == remoteProducerId);
        } catch (e) {
          participant = null;
        }

        if (participant != null &&
            participant.name.isNotEmpty &&
            participant.name != hostLabel) {
          // Add the new video stream to allVideoStreams
          allVideoStreams.add(
            Stream(
                producerId: remoteProducerId, stream: nStream, socket_: nsock),
          );
          updateAllVideoStreams(allVideoStreams);
        }

        // Find the admin participant

        if (participant != null) {
          final String name = participant.name;
          // Add to streamNames
          final newStreamName = Stream(
            producerId: remoteProducerId,
            name: name,
          );
          streamNames.add(newStreamName);
          updateStreamNames(streamNames);
        }

        // If not screenshare, filter out admin streams
        if (!shareScreenStarted) {
          Participant? admin = participants.firstWhere(
            (p) => (p.isAdmin == true || p.isHost == true) && p.islevel == '2',
            orElse: () => Participant(
              audioID: '',
              videoID: '',
              name: '',
              isAdmin: false,
              islevel: '0',
            ),
          );

          if (admin.videoID.isNotEmpty) {
            final String adminVidID = admin.videoID;
            if (adminVidID.isNotEmpty) {
              // Backup oldAllStreams
              List<Stream> oldAllStreams_ = [];
              if (oldAllStreams.isNotEmpty) {
                oldAllStreams_ = oldAllStreams;
              }

              // Filter oldAllStreams for adminVidID
              oldAllStreams = allVideoStreams
                  .where((s) => s.producerId == adminVidID)
                  .toList();
              updateOldAllStreams(oldAllStreams);

              if (oldAllStreams.isEmpty) {
                oldAllStreams = oldAllStreams_;
                updateOldAllStreams(oldAllStreams);
              }

              // Remove adminVidID streams from allVideoStreams
              allVideoStreams = allVideoStreams
                  .where((s) => s.producerId != adminVidID)
                  .toList();
              updateAllVideoStreams(allVideoStreams);

              // If the resumed producer is the admin, update main window
              if (remoteProducerId == adminVidID) {
                updateUpdateMainWindow(true);
              }
            }

            // Update gotAllVids flag
            updateGotAllVids(true);
          }
        } else {
          // Check if the videoID is either that of the admin or that of the screen participant
          Participant? admin = participants.firstWhere(
            (p) => p.islevel == '2',
            orElse: () => Participant(
              audioID: '',
              videoID: '',
              name: '',
              isAdmin: false,
              islevel: '0',
            ),
          );

          Participant? screenParticipant = participants.firstWhere(
            (p) => p.ScreenID == screenId,
            orElse: () => Participant(
              audioID: '',
              videoID: '',
              name: '',
              isAdmin: false,
              islevel: '0',
            ),
          );

          String? adminVidID;
          if (admin.name.isNotEmpty) {
            adminVidID = admin.videoID;
          }

          String? screenParticipantVidID;
          if (screenParticipant.name.isNotEmpty) {
            screenParticipantVidID = screenParticipant.videoID;
          }

          if ((adminVidID != null && adminVidID.isNotEmpty) ||
              (screenParticipantVidID != null &&
                  screenParticipantVidID.isNotEmpty)) {
            if (adminVidID == remoteProducerId ||
                screenParticipantVidID == remoteProducerId) {
              final optionsReorder = ReorderStreamsOptions(
                parameters: updatedParams,
              );
              await reorderStreams(
                optionsReorder,
              );
              return;
            }
          }
        }

        // Update the UI based on lockScreen and shareScreenStarted flags
        if (lockScreen || shareScreenStarted) {
          updateDeferReceive(true);
          if (!firstAll) {
            final optionsReorder = ReorderStreamsOptions(
              add: false,
              screenChanged: true,
              parameters: updatedParams,
            );
            await reorderStreams(
              optionsReorder,
            );

            if (!kIsWeb) {
              await Future.delayed(const Duration(milliseconds: 1000));
              await reorderStreams(
                optionsReorder,
              );
            }
          }
        } else {
          await reorderStreams(
            ReorderStreamsOptions(
              parameters: updatedParams,
            ),
          );

          if (!kIsWeb) {
            await Future.delayed(const Duration(milliseconds: 1000));
            await reorderStreams(
              ReorderStreamsOptions(
                screenChanged: true,
                parameters: updatedParams,
              ),
            );
          }
        }
      }
    }
  } catch (error) {
    if (kDebugMode) {
      print('MediaSFU - consumerResumed error: $error');
    }
    // Optionally, you can rethrow the error or handle it as needed
    // throw error;
  }
}