Skip to content

Commit

Permalink
Merge branch 'td/voipFixesJuly' into 'main'
Browse files Browse the repository at this point in the history
fix: do not proceed call if getUserMedia fails

Closes famedly/company/product-management#1046 and famedly/company/product-management#1052

See merge request famedly/company/frontend/famedlysdk!1321
  • Loading branch information
td-famedly committed Jul 10, 2023
2 parents 8f44c7f + 66a5378 commit 23c6928
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 73 deletions.
91 changes: 45 additions & 46 deletions lib/src/voip/call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class CallSession {
final CachedStreamController<CallSession> onCallReplaced =
CachedStreamController();

final CachedStreamController<CallSession> onCallHangup =
final CachedStreamController<CallSession> onCallHangupNotifierForGroupCalls =
CachedStreamController();

final CachedStreamController<CallState> onCallStateChanged =
Expand Down Expand Up @@ -435,6 +435,7 @@ class CallSession {
Timer? inviteTimer;
Timer? ringingTimer;

// outgoing call
Future<void> initOutboundCall(CallType type) async {
await _preparePeerConnection();
setCallState(CallState.kCreateOffer);
Expand All @@ -444,6 +445,7 @@ class CallSession {
}
}

// incoming call
Future<void> initWithInvite(CallType type, RTCSessionDescription offer,
SDPStreamMetadata? metadata, int lifetime, bool isGroupCall) async {
if (!isGroupCall) {
Expand Down Expand Up @@ -491,6 +493,12 @@ class CallSession {
final stream = await _getUserMedia(type);
if (stream != null) {
await addLocalStream(stream, SDPStreamMetadataPurpose.Usermedia);
} else {
// we don't have a localstream, call probably crashed
// for sanity
if (state == CallState.kEnded) {
return;
}
}
}

Expand Down Expand Up @@ -597,10 +605,6 @@ class CallSession {
// Now we wait for the negotiationneeded event
}

void initWithHangup() {
setCallState(CallState.kEnded);
}

Future<void> onAnswerReceived(
RTCSessionDescription answer, SDPStreamMetadata? metadata) async {
if (metadata != null) {
Expand Down Expand Up @@ -658,9 +662,9 @@ class CallSession {
type: answer.type!);
await pc!.setLocalDescription(answer);
}
} catch (e) {
_getLocalOfferFailed(e);
Logs().e('[VOIP] onNegotiateReceived => ${e.toString()}');
} catch (e, s) {
Logs().e('[VOIP] onNegotiateReceived => ', e, s);
await _getLocalOfferFailed(e);
return;
}

Expand Down Expand Up @@ -740,8 +744,8 @@ class CallSession {
if (pc != null && inviteOrAnswerSent && remotePartyId != null) {
try {
await pc!.addCandidate(candidate);
} catch (e) {
Logs().e('[VOIP] onCandidatesReceived => ${e.toString()}');
} catch (e, s) {
Logs().e('[VOIP] onCandidatesReceived => ', e, s);
}
} else {
remoteCandidates.add(candidate);
Expand Down Expand Up @@ -810,8 +814,8 @@ class CallSession {
await _removeStream(localScreenSharingStream!.stream!);
fireCallEvent(CallEvent.kFeedsChanged);
return false;
} catch (e) {
Logs().e('[VOIP] stopping screen sharing track failed', e);
} catch (e, s) {
Logs().e('[VOIP] stopping screen sharing track failed', e, s);
return false;
}
}
Expand Down Expand Up @@ -1130,12 +1134,11 @@ class CallSession {
/// Reject a call
/// This used to be done by calling hangup, but is a separate method and protocol
/// event as of MSC2746.
///
Future<void> reject({String? reason, bool shouldEmit = true}) async {
// stop play ringtone
await voip.delegate.stopRingtone();
if (state != CallState.kRinging && state != CallState.kFledgling) {
Logs().e('[VOIP] Call must be in \'ringing|fledgling\' state to reject!');
Logs().e(
'[VOIP] Call must be in \'ringing|fledgling\' state to reject! (current state was: ${state.toString()}) Calling hangup instead');
await hangup(reason, shouldEmit);
return;
}
Logs().d('[VOIP] Rejecting call: $callId');
Expand All @@ -1146,12 +1149,9 @@ class CallSession {
}
}

Future<void> hangup([String? reason, bool suppressEvent = false]) async {
// stop play ringtone
await voip.delegate.stopRingtone();

Future<void> hangup([String? reason, bool shouldEmit = true]) async {
await terminate(
CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent);
CallParty.kLocal, reason ?? CallErrorCode.UserHangup, shouldEmit);

try {
final res =
Expand All @@ -1174,11 +1174,11 @@ class CallSession {
}

Future<void> terminate(
CallParty party, String reason, bool shouldEmit) async {
if (state == CallState.kEnded) {
return;
}

CallParty party,
String reason,
bool shouldEmit,
) async {
Logs().d('[VOIP] terminating call');
inviteTimer?.cancel();
inviteTimer = null;

Expand All @@ -1195,9 +1195,9 @@ class CallSession {
hangupParty = party;
hangupReason = reason;

if (shouldEmit) {
setCallState(CallState.kEnded);
}
// don't see any reason to wrap this with shouldEmit atm,
// looks like a local state change only
setCallState(CallState.kEnded);

if (!isGroupCall) {
if (callId != voip.currentCID) return;
Expand All @@ -1209,7 +1209,7 @@ class CallSession {

await cleanUp();
if (shouldEmit) {
onCallHangup.add(this);
onCallHangupNotifierForGroupCalls.add(this);
await voip.delegate.handleCallEnded(this);
fireCallEvent(CallEvent.kHangup);
if ((party == CallParty.kRemote && missedCall)) {
Expand Down Expand Up @@ -1273,7 +1273,6 @@ class CallSession {
// just incase we ended the call but already sent the invite
if (state == CallState.kEnded) {
await hangup(CallErrorCode.Replaced, false);
setCallState(CallState.kEnded);
return;
}
inviteOrAnswerSent = true;
Expand Down Expand Up @@ -1313,7 +1312,7 @@ class CallSession {
final offer = await pc!.createOffer({});
await _gotLocalOffer(offer);
} catch (e) {
_getLocalOfferFailed(e);
await _getLocalOfferFailed(e);
return;
} finally {
makingOffer = false;
Expand Down Expand Up @@ -1377,9 +1376,9 @@ class CallSession {
}
}

void onAnsweredElsewhere() {
Future<void> onAnsweredElsewhere() async {
Logs().d('Call ID $callId answered elsewhere');
terminate(CallParty.kRemote, CallErrorCode.AnsweredElsewhere, true);
await terminate(CallParty.kRemote, CallErrorCode.AnsweredElsewhere, true);
}

Future<void> cleanUp() async {
Expand All @@ -1388,17 +1387,17 @@ class CallSession {
await stream.dispose();
}
streams.clear();
} catch (e) {
Logs().e('[VOIP] cleaning up streams failed', e);
} catch (e, s) {
Logs().e('[VOIP] cleaning up streams failed', e, s);
}

try {
if (pc != null) {
await pc!.close();
await pc!.dispose();
}
} catch (e) {
Logs().e('[VOIP] removing pc failed', e);
} catch (e, s) {
Logs().e('[VOIP] removing pc failed', e, s);
}
}

Expand Down Expand Up @@ -1468,7 +1467,7 @@ class CallSession {
try {
return await voip.delegate.mediaDevices.getUserMedia(mediaConstraints);
} catch (e) {
_getUserMediaFailed(e);
await _getUserMediaFailed(e);
}
return null;
}
Expand All @@ -1481,7 +1480,7 @@ class CallSession {
try {
return await voip.delegate.mediaDevices.getDisplayMedia(mediaConstraints);
} catch (e) {
_getUserMediaFailed(e);
await _getUserMediaFailed(e);
}
return null;
}
Expand Down Expand Up @@ -1614,27 +1613,27 @@ class CallSession {
}
}

void _getLocalOfferFailed(dynamic err) {
Future<void> _getLocalOfferFailed(dynamic err) async {
Logs().e('Failed to get local offer ${err.toString()}');
fireCallEvent(CallEvent.kError);
lastError = CallError(
CallErrorCode.LocalOfferFailed, 'Failed to get local offer!', err);
terminate(CallParty.kLocal, CallErrorCode.LocalOfferFailed, false);
await terminate(CallParty.kLocal, CallErrorCode.LocalOfferFailed, false);
}

void _getUserMediaFailed(dynamic err) {
Future<void> _getUserMediaFailed(dynamic err) async {
if (state != CallState.kConnected) {
Logs().w('Failed to get user media - ending call ${err.toString()}');
fireCallEvent(CallEvent.kError);
lastError = CallError(
CallErrorCode.NoUserMedia,
'Couldn\'t start capturing media! Is your microphone set up and does this app have permission?',
err);
terminate(CallParty.kLocal, CallErrorCode.NoUserMedia, false);
await terminate(CallParty.kLocal, CallErrorCode.NoUserMedia, false);
}
}

void onSelectAnswerReceived(String? selectedPartyId) {
Future<void> onSelectAnswerReceived(String? selectedPartyId) async {
if (direction != CallDirection.kIncoming) {
Logs().w('Got select_answer for an outbound call: ignoring');
return;
Expand All @@ -1649,7 +1648,7 @@ class CallSession {
Logs().w(
'Got select_answer for party ID $selectedPartyId: we are party ID $localPartyId.');
// The other party has picked somebody else's answer
terminate(CallParty.kRemote, CallErrorCode.AnsweredElsewhere, true);
await terminate(CallParty.kRemote, CallErrorCode.AnsweredElsewhere, true);
}
}

Expand Down
5 changes: 3 additions & 2 deletions lib/src/voip/conn_tester.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ class ConnectionTester {
}
return false;
});
} catch (e) {
Logs().e('[VOIP] ConnectionTester Error while testing TURN server: $e');
} catch (e, s) {
Logs()
.e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s);
}

dispose();
Expand Down
12 changes: 6 additions & 6 deletions lib/src/voip/group_call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,8 @@ class GroupCall {
};
try {
return await voip.delegate.mediaDevices.getDisplayMedia(mediaConstraints);
} catch (e) {
setState(GroupCallState.LocalCallFeedUninitialized);
} catch (e, s) {
Logs().e('[VOIP] _getDisplayMedia failed because,', e, s);
}
return Null as MediaStream;
}
Expand Down Expand Up @@ -627,10 +627,10 @@ class GroupCall {
await sendMemberStateEvent();

return true;
} catch (error) {
Logs().e('Enabling screensharing error', error);
} catch (e, s) {
Logs().e('Enabling screensharing error', e, s);
lastError = GroupCallError(GroupCallErrorCode.NoUserMedia,
'Failed to get screen-sharing stream: ', error);
'Failed to get screen-sharing stream: ', e);
onGroupCallEvent.add(GroupCallEvent.Error);
return false;
}
Expand Down Expand Up @@ -994,7 +994,7 @@ class GroupCall {
await onStreamsChanged(call);
});

call.onCallHangup.stream.listen((event) async {
call.onCallHangupNotifierForGroupCalls.stream.listen((event) async {
await onCallHangup(call);
});

Expand Down
8 changes: 4 additions & 4 deletions lib/src/voip/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ Future<void> stopMediaStream(MediaStream? stream) async {
for (final track in stream.getTracks()) {
try {
await track.stop();
} catch (e) {
Logs().e('[VOIP] stopping track ${track.id} failed', e);
} catch (e, s) {
Logs().e('[VOIP] stopping track ${track.id} failed', e, s);
}
}
try {
await stream.dispose();
} catch (e) {
Logs().e('[VOIP] disposing stream ${stream.id} failed', e);
} catch (e, s) {
Logs().e('[VOIP] disposing stream ${stream.id} failed', e, s);
}
}
}
Expand Down
14 changes: 7 additions & 7 deletions lib/src/voip/voip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ class VoIP {
await delegate.stopRingtone();
}
if (call.state == CallState.kRinging) {
call.onAnsweredElsewhere();
await call.onAnsweredElsewhere();
}
return;
}
Expand Down Expand Up @@ -368,7 +368,7 @@ class VoIP {
}
await call.onRejectReceived(content['reason']);
} else {
Logs().v('[VOIP] onCallHangup: Session [$callId] not found!');
Logs().v('[VOIP] onCallReject: Session [$callId] not found!');
}
}

Expand Down Expand Up @@ -408,7 +408,7 @@ class VoIP {
'Ignoring call select answer for room $roomId claiming to be for call in room ${call.room.id}');
return;
}
call.onSelectAnswerReceived(selectedPartyId);
await call.onSelectAnswerReceived(selectedPartyId);
}
}

Expand Down Expand Up @@ -486,8 +486,8 @@ class VoIP {
}
await call.onNegotiateReceived(metadata,
RTCSessionDescription(description['sdp'], description['type']));
} catch (err) {
Logs().e('Failed to complete negotiation ${err.toString()}');
} catch (e, s) {
Logs().e('Failed to complete negotiation', e, s);
}
}
}
Expand All @@ -498,8 +498,8 @@ class VoIP {
if (session['media'].indexWhere((e) => e['type'] == 'video') != -1) {
return CallType.kVideo;
}
} catch (err) {
Logs().e('Failed to getCallType ${err.toString()}');
} catch (e, s) {
Logs().e('Failed to getCallType', e, s);
}

return CallType.kVoice;
Expand Down
Loading

0 comments on commit 23c6928

Please sign in to comment.