streamSuccessVideo function
- StreamSuccessVideoOptions options
Handles successful video streaming setup by initializing video transports, managing UI states, and updating participant information to reflect the video status.
Function Overview
- Video Transport Management: Creates or connects the video transport, ensuring the user can stream video successfully.
- UI and Participant Updates: Updates local and remote participants' view, adjusts display modes, and reorders streams if necessary.
- Device Management: Configures device settings, codec, and constraints based on the user's device and environment.
Parameters:
options
(StreamSuccessVideoOptions
): Contains:stream
(MediaStream
): The new video stream.videoConstraints
(Map<String, dynamic>?
): Optional constraints for video streaming.parameters
(StreamSuccessVideoParameters
): Configuration for setting up video streaming, including:socket
: (io.Socket
): Socket instance for server communication.participants
: (List<Participant>
): List of participants to update the video state.transportCreated
,transportCreatedVideo
: (bool
): Flags indicating if the transports have been created.localStreamVideo
: (MediaStream?
): Local stream used for video streaming.videoParams
: (ProducerOptionsType
): Options for the video producer, including encoding and codec settings.showAlert
: (ShowAlert?
): Optional function to display alerts to the user.
Steps:
-
Stream Initialization:
- Sets
localStreamVideo
to the providedstream
and updates the mainlocalStream
to include the video track. - Retrieves video track settings, such as device ID and facing mode, and updates state accordingly.
- Sets
-
Transport Management:
- Checks if a video transport exists; if not, creates a new transport. Otherwise, connects the existing transport with the stream.
- Removes VP9 codec for optimal compatibility, supporting only VP8 and H264.
-
Participant and UI Updates:
- Updates participant list to mark the current participant's video as "on."
- Modifies
videoAction
andvideoAlreadyOn
states to reflect successful video streaming. - Adjusts the display for the main window if the user is a host.
-
Error Handling:
- Displays an alert if any issues arise during setup, such as missing camera access, and logs errors for debugging.
Example Usage:
final parameters = StreamSuccessVideoParameters(
socket: io.Socket(),
participants: [],
transportCreated: false,
videoAlreadyOn: false,
videoAction: false,
localStreamVideo: null,
videoParams: ProducerOptionsType(),
// Additional parameters and functions for setup...
);
await streamSuccessVideo(
StreamSuccessVideoOptions(
stream: videoStream,
videoConstraints: videoConstraints,
parameters: parameters,
),
);
Error Handling:
- Logs any errors encountered during the setup process for debugging.
- Displays an alert with
showAlert
if provided, detailing issues like camera access failure.
Notes:
- This function reorders streams and configures video parameters based on the user's level, device capabilities, and event type.
Implementation
Future<void> streamSuccessVideo(
StreamSuccessVideoOptions options,
) async {
final MediaStream stream = options.stream;
final StreamSuccessVideoParameters parameters = options.parameters;
try {
// Destructure parameters
io.Socket? socket = parameters.socket;
List<Participant> participants = parameters.participants;
MediaStream? localStream = parameters.localStream;
bool transportCreated = parameters.transportCreated;
bool transportCreatedVideo = parameters.transportCreatedVideo;
bool videoAlreadyOn = parameters.videoAlreadyOn;
bool videoAction = parameters.videoAction;
ProducerOptionsType? videoParams = parameters.videoParams;
MediaStream? localStreamVideo = parameters.localStreamVideo;
String defVideoID = parameters.defVideoID;
String userDefaultVideoInputDevice = parameters.userDefaultVideoInputDevice;
ProducerOptionsType? params = parameters.params;
String islevel = parameters.islevel;
String member = parameters.member;
bool updateMainWindow = parameters.updateMainWindow;
bool lockScreen = parameters.lockScreen;
bool shared = parameters.shared;
bool shareScreenStarted = parameters.shareScreenStarted;
ProducerOptionsType? vParams = parameters.vParams;
ProducerOptionsType? hParams = parameters.hParams;
bool allowed = parameters.allowed;
String currentFacingMode = parameters.currentFacingMode;
Device? device = parameters.device;
// Update functions
void Function(bool created) updateTransportCreatedVideo =
parameters.updateTransportCreatedVideo;
void Function(bool videoOn) updateVideoAlreadyOn =
parameters.updateVideoAlreadyOn;
void Function(bool videoAction) updateVideoAction =
parameters.updateVideoAction;
void Function(MediaStream? stream) updateLocalStream =
parameters.updateLocalStream;
void Function(MediaStream? stream) updateLocalStreamVideo =
parameters.updateLocalStreamVideo;
void Function(String id) updateDefVideoID = parameters.updateDefVideoID;
void Function(String device) updateUserDefaultVideoInputDevice =
parameters.updateUserDefaultVideoInputDevice;
void Function(String mode) updateCurrentFacingMode =
parameters.updateCurrentFacingMode;
void Function(bool allowed) updateAllowed = parameters.updateAllowed;
void Function(bool updateMainWindow) updateUpdateMainWindow =
parameters.updateUpdateMainWindow;
void Function(List<Participant> participants) updateParticipants =
parameters.updateParticipants;
void Function(ProducerOptionsType params) updateVideoParams =
parameters.updateVideoParams;
// Media functions
CreateSendTransportType createSendTransport =
parameters.createSendTransport;
ConnectSendTransportVideoType connectSendTransportVideo =
parameters.connectSendTransportVideo;
ReorderStreamsType reorderStreams = parameters.reorderStreams;
SleepType sleep = parameters.sleep;
ShowAlert? showAlert = parameters.showAlert;
// Update the local video stream
localStreamVideo = stream;
updateLocalStreamVideo(localStreamVideo);
if (localStream == null) {
localStream = stream;
updateLocalStream(localStream);
} else {
// Remove existing video tracks from localStream
for (MediaStreamTrack track
in List<MediaStreamTrack>.from(localStream.getVideoTracks())) {
try {
await localStream.removeTrack(track);
} catch (_) {}
}
// Add the new video track to the localStream
localStream.addTrack(stream.getVideoTracks().first);
updateLocalStream(localStream);
}
// Get video track settings
MediaStreamTrack videoTracked = localStream.getVideoTracks().first;
try {
defVideoID = videoTracked.getSettings()['deviceId'] ?? '';
userDefaultVideoInputDevice = defVideoID;
currentFacingMode = videoTracked.getSettings()['facingMode'] ?? 'user';
if (defVideoID.isNotEmpty) {
updateDefVideoID(defVideoID);
}
if (userDefaultVideoInputDevice.isNotEmpty) {
updateUserDefaultVideoInputDevice(userDefaultVideoInputDevice);
}
if (currentFacingMode.isNotEmpty) {
updateCurrentFacingMode(currentFacingMode);
}
} catch (_) {}
// Update allowed state
allowed = true;
updateAllowed(allowed);
try {
// Apply video constraints
if (islevel == '2') {
if (!shared || !shareScreenStarted) {
params = hParams;
} else {
params = vParams;
}
} else {
params = vParams;
}
// Remove VP9 codec from the video codecs; support only VP8 and H264
RtpCodecCapability? codec = device?.rtpCapabilities.codecs.firstWhere(
(codec) =>
codec.mimeType.toLowerCase() != 'video/vp9' &&
codec.kind == RTCRtpMediaType.RTCRtpMediaTypeVideo,
orElse: () => RtpCodecCapability(
mimeType: 'video/vp8',
kind: RTCRtpMediaType.RTCRtpMediaTypeVideo,
clockRate: 90000,
parameters: {},
),
);
// Create videoParams
params!.codec = codec;
params.stream = stream;
videoParams = params;
updateVideoParams(videoParams);
// Create or connect transport
if (!transportCreated) {
parameters.updateVideoParams(videoParams);
final optionsCreate = CreateSendTransportOptions(
option: 'video',
parameters: parameters,
videoConstraints: options.videoConstraints,
);
await createSendTransport(
optionsCreate,
);
} else {
// Close existing producer if any
if (parameters.videoProducer != null) {
parameters.videoProducer!.close();
await sleep(SleepOptions(
ms: 1000,
));
}
parameters.updateVideoParams(videoParams);
final optionsConnect = ConnectSendTransportVideoOptions(
videoParams: videoParams,
videoConstraints: options.videoConstraints,
parameters: parameters,
);
await connectSendTransportVideo(
optionsConnect,
);
}
} catch (error) {
showAlert!(
message: 'Error sharing video: make sure you have a camera connected',
type: 'danger',
duration: 3000,
);
if (kDebugMode) {
print("Error in video streaming setup: $error");
}
}
// Update videoAlreadyOn state
videoAlreadyOn = true;
updateVideoAlreadyOn(videoAlreadyOn);
// Update video action state
if (videoAction == true) {
videoAction = false;
updateVideoAction(videoAction);
}
// Update display screen if host
if (islevel == '2') {
updateMainWindow = true;
updateUpdateMainWindow(updateMainWindow);
}
// Update participants array to reflect the change
for (var participant in participants) {
if (participant['socketId'] == socket!.id && participant.name == member) {
participant.videoOn = true;
}
}
updateParticipants(participants);
// Update transport created state
transportCreatedVideo = true;
updateTransportCreatedVideo(transportCreatedVideo);
// Reupdate the screen display
if (lockScreen) {
parameters.updateVideoAlreadyOn(videoAlreadyOn);
final optionsReorder = ReorderStreamsOptions(
add: true,
screenChanged: true,
parameters: parameters,
);
await reorderStreams(
optionsReorder,
);
if (!kIsWeb) {
await Future.delayed(const Duration(milliseconds: 1500));
await reorderStreams(
optionsReorder,
);
}
} else {
parameters.updateVideoAlreadyOn(videoAlreadyOn);
final optionsReorder = ReorderStreamsOptions(
add: false,
screenChanged: true,
parameters: parameters,
);
await reorderStreams(
optionsReorder,
);
if (!kIsWeb) {
await Future.delayed(const Duration(milliseconds: 1500));
await reorderStreams(
optionsReorder,
);
}
}
} catch (error) {
if (kDebugMode) {
print('MediaSFU - streamSuccessVideo error: $error');
}
// Optionally retshrow or handle the error further
// throw error;
}
}