onCallInvite method
Implementation
Future<void> onCallInvite(Room room, String remoteUserId,
String? remoteDeviceId, Map<String, dynamic> content) async {
Logs().v(
'[VOIP] onCallInvite $remoteUserId:$remoteDeviceId => ${client.userID}:${client.deviceID}, \ncontent => ${content.toString()}');
final String callId = content['call_id'];
final int lifetime = content['lifetime'];
final String? confId = content['conf_id'];
final call = calls[VoipId(roomId: room.id, callId: callId)];
Logs().d(
'[glare] got new call ${content.tryGet('call_id')} and currently room id is mapped to ${incomingCallRoomId.tryGet(room.id)}');
if (call != null && call.state == CallState.kEnded) {
// Session already exist.
Logs().v('[VOIP] onCallInvite: Session [$callId] already exist.');
return;
}
final inviteeUserId = content['invitee'];
if (inviteeUserId != null && inviteeUserId != localParticipant?.userId) {
Logs().w('[VOIP] Ignoring call, meant for user $inviteeUserId');
return; // This invite was meant for another user in the room
}
final inviteeDeviceId = content['invitee_device_id'];
if (inviteeDeviceId != null &&
inviteeDeviceId != localParticipant?.deviceId) {
Logs().w('[VOIP] Ignoring call, meant for device $inviteeDeviceId');
return; // This invite was meant for another device in the room
}
if (content['capabilities'] != null) {
final capabilities = CallCapabilities.fromJson(content['capabilities']);
Logs().v(
'[VOIP] CallCapabilities: dtmf => ${capabilities.dtmf}, transferee => ${capabilities.transferee}');
}
var callType = CallType.kVoice;
SDPStreamMetadata? sdpStreamMetadata;
if (content[sdpStreamMetadataKey] != null) {
sdpStreamMetadata =
SDPStreamMetadata.fromJson(content[sdpStreamMetadataKey]);
sdpStreamMetadata.sdpStreamMetadatas
.forEach((streamId, SDPStreamPurpose purpose) {
Logs().v(
'[VOIP] [$streamId] => purpose: ${purpose.purpose}, audioMuted: ${purpose.audio_muted}, videoMuted: ${purpose.video_muted}');
if (!purpose.video_muted) {
callType = CallType.kVideo;
}
});
} else {
callType = getCallType(content['offer']['sdp']);
}
final opts = CallOptions(
voip: this,
callId: callId,
groupCallId: confId,
dir: CallDirection.kIncoming,
type: callType,
room: room,
localPartyId: localPartyId,
iceServers: await getIceServers(),
);
final newCall = createNewCall(opts);
/// both invitee userId and deviceId are set here because there can be
/// multiple devices from same user in a call, so we specifiy who the
/// invite is for
newCall.remoteUserId = remoteUserId;
newCall.remoteDeviceId = remoteDeviceId;
newCall.remotePartyId = content['party_id'];
newCall.remoteSessionId = content['sender_session_id'];
// newCall.remoteSessionId = remoteParticipant.sessionId;
if (!delegate.canHandleNewCall &&
(confId == null ||
currentGroupCID != VoipId(roomId: room.id, callId: confId))) {
Logs().v(
'[VOIP] onCallInvite: Unable to handle new calls, maybe user is busy.');
// no need to emit here because handleNewCall was never triggered yet
await newCall.reject(reason: CallErrorCode.userBusy, shouldEmit: false);
await delegate.handleMissedCall(newCall);
return;
}
final offer = RTCSessionDescription(
content['offer']['sdp'],
content['offer']['type'],
);
/// play ringtone. We decided to play the ringtone before adding the call to
/// the incoming call stream because getUserMedia from initWithInvite fails
/// on firefox unless the tab is in focus. We should atleast be able to notify
/// the user about an incoming call
///
/// Autoplay on firefox still needs interaction, without which all notifications
/// could be blocked.
if (confId == null) {
await delegate.playRingtone();
}
// When getUserMedia throws an exception, we handle it by terminating the call,
// and all this happens inside initWithInvite. If we set currentCID after
// initWithInvite, we might set it to callId even after it was reset to null
// by terminate.
currentCID = VoipId(roomId: room.id, callId: callId);
await newCall.initWithInvite(
callType, offer, sdpStreamMetadata, lifetime, confId != null);
// Popup CallingPage for incoming call.
if (confId == null && !newCall.callHasEnded) {
await delegate.handleNewCall(newCall);
}
if (confId != null) {
// the stream is used to monitor incoming peer calls in a mesh call
onIncomingCall.add(newCall);
}
}