fix: Fix the call function in flutter and dart.

This commit is contained in:
cloudwebrtc 2021-12-01 00:47:11 +08:00
parent 7efe123274
commit 1f519703a8
1 changed files with 84 additions and 46 deletions

View File

@ -11,7 +11,7 @@ abstract class WebRTCDelegate {
MediaDevices get mediaDevices; MediaDevices get mediaDevices;
Future<RTCPeerConnection> createPeerConnection( Future<RTCPeerConnection> createPeerConnection(
Map<String, dynamic> configuration, Map<String, dynamic> configuration,
[Map<String, dynamic> constraints]); [Map<String, dynamic> constraints = const {}]);
VideoRenderer createRenderer(); VideoRenderer createRenderer();
void playRingtone(); void playRingtone();
void stopRingtone(); void stopRingtone();
@ -40,6 +40,7 @@ class WrappedMediaStream {
bool videoMuted; bool videoMuted;
final Client client; final Client client;
VideoRenderer renderer; VideoRenderer renderer;
final bool isWeb;
/// for debug /// for debug
String get title => '$displayName:$purpose:a[$audioMuted]:v[$videoMuted]'; String get title => '$displayName:$purpose:a[$audioMuted]:v[$videoMuted]';
@ -55,7 +56,8 @@ class WrappedMediaStream {
required this.purpose, required this.purpose,
required this.client, required this.client,
required this.audioMuted, required this.audioMuted,
required this.videoMuted}); required this.videoMuted,
required this.isWeb});
/// Initialize the video renderer /// Initialize the video renderer
Future<void> initialize() async { Future<void> initialize() async {
@ -70,6 +72,11 @@ class WrappedMediaStream {
Future<void> dispose() async { Future<void> dispose() async {
renderer.srcObject = null; renderer.srcObject = null;
if (isLocal() && stream != null) { if (isLocal() && stream != null) {
if (isWeb) {
stream!.getTracks().forEach((element) {
element.stop();
});
}
await stream?.dispose(); await stream?.dispose();
stream = null; stream = null;
} }
@ -289,7 +296,7 @@ class CallSession {
bool makingOffer = false; bool makingOffer = false;
bool ignoreOffer = false; bool ignoreOffer = false;
String facingMode = 'user'; String facingMode = 'user';
late Client client; Client get client => opts.room.client;
String? remotePartyId; String? remotePartyId;
late User remoteUser; late User remoteUser;
late CallParty hangupParty; late CallParty hangupParty;
@ -298,25 +305,48 @@ class CallSession {
SDPStreamMetadata? remoteSDPStreamMetadata; SDPStreamMetadata? remoteSDPStreamMetadata;
List<RTCRtpSender> usermediaSenders = []; List<RTCRtpSender> usermediaSenders = [];
List<RTCRtpSender> screensharingSenders = []; List<RTCRtpSender> screensharingSenders = [];
Map<String, WrappedMediaStream> streams = <String, WrappedMediaStream>{}; List<WrappedMediaStream> streams = <WrappedMediaStream>[];
List<WrappedMediaStream> get getLocalStreams => List<WrappedMediaStream> get getLocalStreams =>
streams.values.where((element) => element.isLocal()).toList(); streams.where((element) => element.isLocal()).toList();
List<WrappedMediaStream> get getRemoteStreams => List<WrappedMediaStream> get getRemoteStreams =>
streams.values.where((element) => !element.isLocal()).toList(); streams.where((element) => !element.isLocal()).toList();
WrappedMediaStream? get localUserMediaStream => getLocalStreams.firstWhere(
(element) => element.purpose == SDPStreamMetadataPurpose.Usermedia, WrappedMediaStream? get localUserMediaStream {
orElse: () => Null as WrappedMediaStream); final stream = getLocalStreams.where(
WrappedMediaStream? get localScreenSharingStream => (element) => element.purpose == SDPStreamMetadataPurpose.Usermedia);
getLocalStreams.firstWhere( if (stream.isNotEmpty) {
(element) => element.purpose == SDPStreamMetadataPurpose.Screenshare, return stream.first;
orElse: () => Null as WrappedMediaStream); }
WrappedMediaStream? get remoteUserMediaStream => getRemoteStreams.firstWhere( return null;
(element) => element.purpose == SDPStreamMetadataPurpose.Usermedia, }
orElse: () => Null as WrappedMediaStream);
WrappedMediaStream? get remoteScreenSharingStream => WrappedMediaStream? get localScreenSharingStream {
getRemoteStreams.firstWhere( final stream = getLocalStreams.where(
(element) => element.purpose == SDPStreamMetadataPurpose.Screenshare, (element) => element.purpose == SDPStreamMetadataPurpose.Screenshare);
orElse: () => Null as WrappedMediaStream); if (stream.isNotEmpty) {
return stream.first;
}
return null;
}
WrappedMediaStream? get remoteUserMediaStream {
final stream = getRemoteStreams.where(
(element) => element.purpose == SDPStreamMetadataPurpose.Usermedia);
if (stream.isNotEmpty) {
return stream.first;
}
return null;
}
WrappedMediaStream? get remoteScreenSharingStream {
final stream = getRemoteStreams.where(
(element) => element.purpose == SDPStreamMetadataPurpose.Screenshare);
if (stream.isNotEmpty) {
return stream.first;
}
return null;
}
final _callStateController = final _callStateController =
StreamController<CallState>.broadcast(sync: true); StreamController<CallState>.broadcast(sync: true);
Stream<CallState> get onCallStateChanged => _callStateController.stream; Stream<CallState> get onCallStateChanged => _callStateController.stream;
@ -520,6 +550,7 @@ class CallSession {
await track.stop(); await track.stop();
} }
localScreenSharingStream!.stopped = true; localScreenSharingStream!.stopped = true;
_removeStream(localScreenSharingStream!.stream!);
emit(CallEvent.kFeedsChanged, streams); emit(CallEvent.kFeedsChanged, streams);
return false; return false;
} }
@ -527,11 +558,10 @@ class CallSession {
void _addLocalStream(MediaStream stream, String purpose, void _addLocalStream(MediaStream stream, String purpose,
{bool addToPeerConnection = true}) async { {bool addToPeerConnection = true}) async {
final WrappedMediaStream? existingStream = getLocalStreams.firstWhere( final existingStream =
(element) => element.purpose == purpose, getLocalStreams.where((element) => element.purpose == purpose);
orElse: () => Null as WrappedMediaStream); if (existingStream.isNotEmpty) {
if (existingStream != null) { existingStream.first.setNewStream(stream);
existingStream.setNewStream(stream);
} else { } else {
final newStream = WrappedMediaStream( final newStream = WrappedMediaStream(
renderer: voip.delegate.createRenderer(), renderer: voip.delegate.createRenderer(),
@ -542,8 +572,10 @@ class CallSession {
client: client, client: client,
audioMuted: stream.getAudioTracks().isEmpty, audioMuted: stream.getAudioTracks().isEmpty,
videoMuted: stream.getVideoTracks().isEmpty, videoMuted: stream.getVideoTracks().isEmpty,
isWeb: voip.delegate.isWeb,
); );
streams[stream.id] = newStream; await newStream.initialize();
streams.add(newStream);
emit(CallEvent.kFeedsChanged, streams); emit(CallEvent.kFeedsChanged, streams);
} }
@ -564,7 +596,7 @@ class CallSession {
if (purpose == SDPStreamMetadataPurpose.Usermedia) { if (purpose == SDPStreamMetadataPurpose.Usermedia) {
speakerOn = type == CallType.kVideo; speakerOn = type == CallType.kVideo;
if (voip.delegate.isWeb && !voip.delegate.isBackgroud) { if (!voip.delegate.isWeb && !voip.delegate.isBackgroud) {
final audioTrack = stream.getAudioTracks()[0]; final audioTrack = stream.getAudioTracks()[0];
audioTrack.enableSpeakerphone(speakerOn); audioTrack.enableSpeakerphone(speakerOn);
} }
@ -586,11 +618,10 @@ class CallSession {
// Try to find a feed with the same purpose as the new stream, // Try to find a feed with the same purpose as the new stream,
// if we find it replace the old stream with the new one // if we find it replace the old stream with the new one
final WrappedMediaStream? existingStream = getRemoteStreams.firstWhere( final existingStream =
(element) => element.purpose == purpose, getRemoteStreams.where((element) => element.purpose == purpose);
orElse: () => Null as WrappedMediaStream); if (existingStream.isNotEmpty) {
if (existingStream != null) { existingStream.first.setNewStream(stream);
existingStream.setNewStream(stream);
} else { } else {
final newStream = WrappedMediaStream( final newStream = WrappedMediaStream(
renderer: voip.delegate.createRenderer(), renderer: voip.delegate.createRenderer(),
@ -601,8 +632,10 @@ class CallSession {
client: client, client: client,
audioMuted: audioMuted, audioMuted: audioMuted,
videoMuted: videoMuted, videoMuted: videoMuted,
isWeb: voip.delegate.isWeb,
); );
streams[stream.id] = newStream; await newStream.initialize();
streams.add(newStream);
} }
emit(CallEvent.kFeedsChanged, streams); emit(CallEvent.kFeedsChanged, streams);
Logs().i('Pushed remote stream (id="${stream.id}", purpose=$purpose)'); Logs().i('Pushed remote stream (id="${stream.id}", purpose=$purpose)');
@ -756,7 +789,8 @@ class CallSession {
setCallState(CallState.kEnded); setCallState(CallState.kEnded);
voip.currentCID = null; voip.currentCID = null;
voip.calls.remove(callId); voip.calls.remove(callId);
cleanUp();
voip.delegate.handleCallEnded(this);
if (shouldEmit) { if (shouldEmit) {
emit(CallEvent.kHangup, this); emit(CallEvent.kHangup, this);
} }
@ -883,7 +917,7 @@ class CallSession {
} }
void cleanUp() async { void cleanUp() async {
streams.forEach((id, stream) { streams.forEach((stream) {
stream.dispose(); stream.dispose();
}); });
streams.clear(); streams.clear();
@ -992,25 +1026,30 @@ class CallSession {
void tryRemoveStopedStreams() { void tryRemoveStopedStreams() {
final removedStreams = <String, WrappedMediaStream>{}; final removedStreams = <String, WrappedMediaStream>{};
streams.forEach((id, stream) { streams.forEach((stream) {
if (stream.stopped) { if (stream.stopped) {
removedStreams[id] = stream; removedStreams[stream.stream!.id] = stream;
} }
}); });
streams.removeWhere((id, stream) => removedStreams.containsKey(id)); streams
.removeWhere((stream) => removedStreams.containsKey(stream.stream!.id));
removedStreams.forEach((id, element) { removedStreams.forEach((id, element) {
_removeStream(id); _removeStream(element.stream!);
}); });
} }
Future<void> _removeStream(String streamId) async { Future<void> _removeStream(MediaStream stream) async {
Logs().v('Removing feed with stream id $streamId'); Logs().v('Removing feed with stream id ${stream.id}');
final removedStream = streams.remove(streamId);
if (removedStream == null) { final it = streams.where((element) => element.stream!.id == stream.id);
Logs().v('Didn\'t find the feed with stream id $streamId to delete'); if (it.isEmpty) {
Logs().v('Didn\'t find the feed with stream id ${stream.id} to delete');
return; return;
} }
await removedStream.dispose(); final wpstream = it.first;
streams.removeWhere((element) => element.stream!.id == stream.id);
emit(CallEvent.kFeedsChanged, streams);
await wpstream.dispose();
} }
Map<String, dynamic> _getOfferAnswerConstraints({bool iceRestart = false}) { Map<String, dynamic> _getOfferAnswerConstraints({bool iceRestart = false}) {
@ -1281,7 +1320,6 @@ class VoIP {
// hangup in any case, either if the other party hung up or we did on another device // hangup in any case, either if the other party hung up or we did on another device
call.terminate(CallParty.kRemote, call.terminate(CallParty.kRemote,
event.content['reason'] ?? CallErrorCode.UserHangup, true); event.content['reason'] ?? CallErrorCode.UserHangup, true);
delegate.handleCallEnded(call);
} else { } else {
Logs().v('[VOIP] onCallHangup: Session [$callId] not found!'); Logs().v('[VOIP] onCallHangup: Session [$callId] not found!');
} }