You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
531 lines
21 KiB
Dart
531 lines
21 KiB
Dart
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:twilio_programmable_video/twilio_programmable_video.dart';
|
|
|
|
class ConferenceRoom with ChangeNotifier {
|
|
final String name;
|
|
final String token;
|
|
final String identity;
|
|
|
|
final StreamController<bool> _onAudioEnabledStreamController = StreamController<bool>.broadcast();
|
|
Stream<bool> onAudioEnabled;
|
|
final StreamController<bool> _onVideoEnabledStreamController = StreamController<bool>.broadcast();
|
|
Stream<bool> onVideoEnabled;
|
|
final StreamController<Exception> _onExceptionStreamController = StreamController<Exception>.broadcast();
|
|
Stream<Exception> onException;
|
|
|
|
final Completer<Room> _completer = Completer<Room>();
|
|
|
|
final List<ParticipantWidget> _participants = [];
|
|
final List<ParticipantBuffer> _participantBuffer = [];
|
|
final List<StreamSubscription> _streamSubscriptions = [];
|
|
final List<RemoteDataTrack> _dataTracks = [];
|
|
final List<String> _messages = [];
|
|
|
|
CameraCapturer _cameraCapturer;
|
|
Room _room;
|
|
Timer _timer;
|
|
|
|
ConferenceRoom({
|
|
@required this.name,
|
|
@required this.token,
|
|
@required this.identity,
|
|
}) {
|
|
onAudioEnabled = _onAudioEnabledStreamController.stream;
|
|
onVideoEnabled = _onVideoEnabledStreamController.stream;
|
|
onException = _onExceptionStreamController.stream;
|
|
}
|
|
|
|
List<ParticipantWidget> get participants {
|
|
return [..._participants];
|
|
}
|
|
|
|
Future<Room> connect() async {
|
|
print('ConferenceRoom.connect()');
|
|
try {
|
|
await TwilioProgrammableVideo.debug(dart: true, native: true);
|
|
await TwilioProgrammableVideo.setSpeakerphoneOn(true);
|
|
|
|
_cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA);
|
|
var connectOptions = ConnectOptions(
|
|
token,
|
|
roomName: name,
|
|
preferredAudioCodecs: [OpusCodec()],
|
|
audioTracks: [LocalAudioTrack(true)],
|
|
dataTracks: [LocalDataTrack()],
|
|
videoTracks: [LocalVideoTrack(true, _cameraCapturer)],
|
|
enableDominantSpeaker: true,
|
|
);
|
|
|
|
_room = await TwilioProgrammableVideo.connect(connectOptions);
|
|
|
|
_streamSubscriptions.add(_room.onConnected.listen(_onConnected));
|
|
_streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure));
|
|
|
|
return _completer.future;
|
|
} catch (err) {
|
|
print(err);
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<void> disconnect() async {
|
|
print('ConferenceRoom.disconnect()');
|
|
if (_timer != null) {
|
|
_timer.cancel();
|
|
}
|
|
await _room.disconnect();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
print('ConferenceRoom.dispose()');
|
|
_disposeStreamsAndSubscriptions();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _disposeStreamsAndSubscriptions() async {
|
|
await _onAudioEnabledStreamController.close();
|
|
await _onVideoEnabledStreamController.close();
|
|
await _onExceptionStreamController.close();
|
|
for (var streamSubscription in _streamSubscriptions) {
|
|
await streamSubscription.cancel();
|
|
}
|
|
}
|
|
|
|
Future<void> sendMessage(String message) async {
|
|
final tracks = _room.localParticipant.localDataTracks;
|
|
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
|
if (localDataTrack == null || _messages.isNotEmpty) {
|
|
print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.');
|
|
_messages.add(message);
|
|
return;
|
|
}
|
|
await localDataTrack.send(message);
|
|
}
|
|
|
|
Future<void> sendBufferMessage(ByteBuffer message) async {
|
|
final tracks = _room.localParticipant.localDataTracks;
|
|
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
|
if (localDataTrack == null) {
|
|
return;
|
|
}
|
|
await localDataTrack.sendBuffer(message);
|
|
}
|
|
|
|
Future<void> toggleVideoEnabled() async {
|
|
final tracks = _room.localParticipant.localVideoTracks;
|
|
final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack;
|
|
if (localVideoTrack == null) {
|
|
print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!');
|
|
return;
|
|
}
|
|
await localVideoTrack.enable(!localVideoTrack.isEnabled);
|
|
|
|
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
var participant = _participants[index];
|
|
_participants.replaceRange(
|
|
index,
|
|
index + 1,
|
|
[
|
|
participant.copyWith(videoEnabled: localVideoTrack.isEnabled),
|
|
],
|
|
);
|
|
print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}');
|
|
_onVideoEnabledStreamController.add(localVideoTrack.isEnabled);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> toggleAudioEnabled() async {
|
|
final tracks = _room.localParticipant.localAudioTracks;
|
|
final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack;
|
|
if (localAudioTrack == null) {
|
|
print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!');
|
|
return;
|
|
}
|
|
await localAudioTrack.enable(!localAudioTrack.isEnabled);
|
|
|
|
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
var participant = _participants[index];
|
|
_participants.replaceRange(
|
|
index,
|
|
index + 1,
|
|
[
|
|
participant.copyWith(audioEnabled: localAudioTrack.isEnabled),
|
|
],
|
|
);
|
|
print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}');
|
|
_onAudioEnabledStreamController.add(localAudioTrack.isEnabled);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> switchCamera() async {
|
|
print('ConferenceRoom.switchCamera()');
|
|
try {
|
|
await _cameraCapturer.switchCamera();
|
|
} on FormatException catch (e) {
|
|
print(
|
|
'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}',
|
|
);
|
|
}
|
|
}
|
|
|
|
void addDummy({Widget child}) {
|
|
print('ConferenceRoom.addDummy()');
|
|
if (_participants.length >= 18) {
|
|
throw PlatformException(
|
|
code: 'ConferenceRoom.maximumReached',
|
|
message: 'Maximum reached',
|
|
details: 'Currently the lay-out can only render a maximum of 18 participants',
|
|
);
|
|
}
|
|
_participants.insert(
|
|
0,
|
|
ParticipantWidget(
|
|
id: (_participants.length + 1).toString(),
|
|
child: child,
|
|
isRemote: true,
|
|
audioEnabled: true,
|
|
videoEnabled: true,
|
|
isDummy: true,
|
|
),
|
|
);
|
|
notifyListeners();
|
|
}
|
|
|
|
void removeDummy() {
|
|
print('ConferenceRoom.removeDummy()');
|
|
var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null);
|
|
if (dummy != null) {
|
|
_participants.remove(dummy);
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
void _onConnected(Room room) {
|
|
print('ConferenceRoom._onConnected => state: ${room.state}');
|
|
|
|
// When connected for the first time, add remote participant listeners
|
|
_streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected));
|
|
_streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected));
|
|
_streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged));
|
|
// Only add ourselves when connected for the first time too.
|
|
_participants.add(
|
|
_buildParticipant(
|
|
child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(),
|
|
id: identity,
|
|
audioEnabled: true,
|
|
videoEnabled: true,
|
|
),
|
|
);
|
|
for (final remoteParticipant in room.remoteParticipants) {
|
|
var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null);
|
|
if (participant == null) {
|
|
print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected');
|
|
_addRemoteParticipantListeners(remoteParticipant);
|
|
}
|
|
}
|
|
// We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in
|
|
// order to be able to use the [send] method.
|
|
_streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished));
|
|
notifyListeners();
|
|
_completer.complete(room);
|
|
|
|
_timer = Timer.periodic(const Duration(minutes: 1), (_) {
|
|
// Let's see if we can send some data over the DataTrack API
|
|
sendMessage('And another minute has passed since I connected...');
|
|
// Also try the ByteBuffer way of sending data
|
|
final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits;
|
|
var bytes = Uint8List.fromList(list);
|
|
sendBufferMessage(bytes.buffer);
|
|
});
|
|
}
|
|
|
|
void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) {
|
|
// Send buffered messages, if any...
|
|
while (_messages.isNotEmpty) {
|
|
var message = _messages.removeAt(0);
|
|
print('Sending buffered message: $message');
|
|
event.localDataTrackPublication.localDataTrack.send(message);
|
|
}
|
|
}
|
|
|
|
void _onConnectFailure(RoomConnectFailureEvent event) {
|
|
print('ConferenceRoom._onConnectFailure: ${event.exception}');
|
|
_completer.completeError(event.exception);
|
|
}
|
|
|
|
void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) {
|
|
print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}');
|
|
var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null);
|
|
if (oldDominantParticipant != null) {
|
|
var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant);
|
|
_participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]);
|
|
}
|
|
|
|
var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid);
|
|
var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant);
|
|
_participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]);
|
|
notifyListeners();
|
|
}
|
|
|
|
void _onParticipantConnected(RoomParticipantConnectedEvent event) {
|
|
print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}');
|
|
_addRemoteParticipantListeners(event.remoteParticipant);
|
|
}
|
|
|
|
void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) {
|
|
print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}');
|
|
_participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid);
|
|
notifyListeners();
|
|
}
|
|
|
|
ParticipantWidget _buildParticipant({
|
|
@required Widget child,
|
|
@required String id,
|
|
@required bool audioEnabled,
|
|
@required bool videoEnabled,
|
|
RemoteParticipant remoteParticipant,
|
|
}) {
|
|
return ParticipantWidget(
|
|
id: remoteParticipant?.sid,
|
|
isRemote: remoteParticipant != null,
|
|
child: child,
|
|
audioEnabled: audioEnabled,
|
|
videoEnabled: videoEnabled,
|
|
);
|
|
}
|
|
|
|
void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) {
|
|
print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}');
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished));
|
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed));
|
|
|
|
_streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished));
|
|
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed));
|
|
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed));
|
|
_streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished));
|
|
_streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed));
|
|
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished));
|
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed));
|
|
}
|
|
|
|
void _onAudioTrackDisabled(RemoteAudioTrackEvent event) {
|
|
print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
|
_setRemoteAudioEnabled(event);
|
|
}
|
|
|
|
void _onAudioTrackEnabled(RemoteAudioTrackEvent event) {
|
|
print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
|
_setRemoteAudioEnabled(event);
|
|
}
|
|
|
|
void _onAudioTrackPublished(RemoteAudioTrackEvent event) {
|
|
print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}');
|
|
}
|
|
|
|
void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
|
_addOrUpdateParticipant(event);
|
|
}
|
|
|
|
void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) {
|
|
print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
|
_onExceptionStreamController.add(
|
|
PlatformException(
|
|
code: 'ConferenceRoom.audioTrackSubscriptionFailed',
|
|
message: 'AudioTrack Subscription Failed',
|
|
details: event.exception.toString(),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) {
|
|
print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
|
}
|
|
|
|
void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}');
|
|
}
|
|
|
|
void _onDataTrackPublished(RemoteDataTrackEvent event) {
|
|
print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}');
|
|
}
|
|
|
|
void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
|
final dataTrack = event.remoteDataTrackPublication.remoteDataTrack;
|
|
_dataTracks.add(dataTrack);
|
|
_streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage));
|
|
_streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage));
|
|
}
|
|
|
|
void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) {
|
|
print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
|
_onExceptionStreamController.add(
|
|
PlatformException(
|
|
code: 'ConferenceRoom.dataTrackSubscriptionFailed',
|
|
message: 'DataTrack Subscription Failed',
|
|
details: event.exception.toString(),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onDataTrackUnpublished(RemoteDataTrackEvent event) {
|
|
print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
|
}
|
|
|
|
void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}');
|
|
}
|
|
|
|
void _onVideoTrackDisabled(RemoteVideoTrackEvent event) {
|
|
print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
|
_setRemoteVideoEnabled(event);
|
|
}
|
|
|
|
void _onVideoTrackEnabled(RemoteVideoTrackEvent event) {
|
|
print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
|
_setRemoteVideoEnabled(event);
|
|
}
|
|
|
|
void _onVideoTrackPublished(RemoteVideoTrackEvent event) {
|
|
print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
|
}
|
|
|
|
void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
|
_addOrUpdateParticipant(event);
|
|
}
|
|
|
|
void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) {
|
|
print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
|
_onExceptionStreamController.add(
|
|
PlatformException(
|
|
code: 'ConferenceRoom.videoTrackSubscriptionFailed',
|
|
message: 'VideoTrack Subscription Failed',
|
|
details: event.exception.toString(),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) {
|
|
print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
|
}
|
|
|
|
void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
|
print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
|
}
|
|
|
|
void _onMessage(RemoteDataTrackStringMessageEvent event) {
|
|
print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}');
|
|
}
|
|
|
|
void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) {
|
|
print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}');
|
|
}
|
|
|
|
void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) {
|
|
if (event.remoteAudioTrackPublication == null) {
|
|
return;
|
|
}
|
|
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
var participant = _participants[index];
|
|
_participants.replaceRange(
|
|
index,
|
|
index + 1,
|
|
[
|
|
participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled),
|
|
],
|
|
);
|
|
notifyListeners();
|
|
}
|
|
|
|
void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) {
|
|
if (event.remoteVideoTrackPublication == null) {
|
|
return;
|
|
}
|
|
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
var participant = _participants[index];
|
|
_participants.replaceRange(
|
|
index,
|
|
index + 1,
|
|
[
|
|
participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled),
|
|
],
|
|
);
|
|
notifyListeners();
|
|
}
|
|
|
|
void _addOrUpdateParticipant(RemoteParticipantEvent event) {
|
|
print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}');
|
|
final participant = _participants.firstWhere(
|
|
(ParticipantWidget participant) => participant.id == event.remoteParticipant.sid,
|
|
orElse: () => null,
|
|
);
|
|
if (participant != null) {
|
|
print('Participant found: ${participant.id}, updating A/V enabled values');
|
|
_setRemoteVideoEnabled(event);
|
|
_setRemoteAudioEnabled(event);
|
|
} else {
|
|
final bufferedParticipant = _participantBuffer.firstWhere(
|
|
(ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid,
|
|
orElse: () => null,
|
|
);
|
|
if (bufferedParticipant != null) {
|
|
_participantBuffer.remove(bufferedParticipant);
|
|
} else if (event is RemoteAudioTrackEvent) {
|
|
print('Audio subscription came first, waiting for the video subscription...');
|
|
_participantBuffer.add(
|
|
ParticipantBuffer(
|
|
id: event.remoteParticipant.sid,
|
|
audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
if (event is RemoteVideoTrackSubscriptionEvent) {
|
|
print('New participant, adding: ${event.remoteParticipant.sid}');
|
|
_participants.insert(
|
|
0,
|
|
_buildParticipant(
|
|
child: event.remoteVideoTrack.widget(),
|
|
id: event.remoteParticipant.sid,
|
|
remoteParticipant: event.remoteParticipant,
|
|
audioEnabled: bufferedParticipant?.audioEnabled ?? true,
|
|
videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true,
|
|
),
|
|
);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
}
|
|
}
|