diff --git a/lib/src/room.dart b/lib/src/room.dart index eea53852..38777b81 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -30,10 +30,6 @@ import '../matrix.dart'; import 'utils/markdown.dart'; import 'utils/marked_unread.dart'; -/// https://github.com/matrix-org/matrix-doc/pull/2746 -/// version 1 -const String voipProtoVersion = '1'; - enum PushRuleState { notify, mentionsOnly, dontNotify } enum JoinRules { public, knock, invite, private } enum GuestAccess { canJoin, forbidden } @@ -1606,290 +1602,6 @@ class Room { Future sendTypingInfo(bool isTyping, {int? timeout}) => setTyping(isTyping, timeout: timeout); - /// This is sent by the caller when they wish to establish a call. - /// [callId] is a unique identifier for the call. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [lifetime] is the time in milliseconds that the invite is valid for. Once the invite age exceeds this value, - /// clients should discard it. They should also no longer show the call as awaiting an answer in the UI. - /// [type] The type of session description. Must be 'offer'. - /// [sdp] The SDP text of the session description. - /// [invitee] The user ID of the person who is being invited. Invites without an invitee field are defined to be - /// intended for any member of the room other than the sender of the event. - /// [party_id] The party ID for call, Can be set to client.deviceId. - Future inviteToCall( - String callId, int lifetime, String party_id, String? invitee, String sdp, - {String type = 'offer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'lifetime': lifetime, - 'offer': {'sdp': sdp, 'type': type}, - if (invitee != null) 'invitee': invitee, - if (capabilities != null) 'capabilities': capabilities.toJson(), - if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), - }; - return await _sendContent( - EventTypes.CallInvite, - content, - txid: txid, - ); - } - - /// The calling party sends the party_id of the first selected answer. - /// - /// Usually after receiving the first answer sdp in the client.onCallAnswer event, - /// save the `party_id`, and then send `CallSelectAnswer` to others peers that the call has been picked up. - /// - /// [callId] is a unique identifier for the call. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - /// [selected_party_id] The party ID for the selected answer. - Future selectCallAnswer( - String callId, int lifetime, String party_id, String selected_party_id, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'lifetime': lifetime, - 'selected_party_id': selected_party_id, - }; - - return await _sendContent( - EventTypes.CallSelectAnswer, - content, - txid: txid, - ); - } - - /// Reject a call - /// [callId] is a unique identifier for the call. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - Future sendCallReject(String callId, int lifetime, String party_id, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'lifetime': lifetime, - }; - - return await _sendContent( - EventTypes.CallReject, - content, - txid: txid, - ); - } - - /// When local audio/video tracks are added/deleted or hold/unhold, - /// need to createOffer and renegotiation. - /// [callId] is a unique identifier for the call. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - Future sendCallNegotiate( - String callId, int lifetime, String party_id, String sdp, - {String type = 'offer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'lifetime': lifetime, - 'description': {'sdp': sdp, 'type': type}, - if (capabilities != null) 'capabilities': capabilities.toJson(), - if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), - }; - return await _sendContent( - EventTypes.CallNegotiate, - content, - txid: txid, - ); - } - - /// This is sent by callers after sending an invite and by the callee after answering. - /// Its purpose is to give the other party additional ICE candidates to try using to communicate. - /// - /// [callId] The ID of the call this event relates to. - /// - /// [version] The version of the VoIP specification this messages adheres to. This specification is version 1. - /// - /// [party_id] The party ID for call, Can be set to client.deviceId. - /// - /// [candidates] Array of objects describing the candidates. Example: - /// - /// ``` - /// [ - /// { - /// "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0", - /// "sdpMLineIndex": 0, - /// "sdpMid": "audio" - /// } - /// ], - /// ``` - Future sendCallCandidates( - String callId, - String party_id, - List> candidates, { - String version = voipProtoVersion, - String? txid, - }) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'candidates': candidates, - }; - return await _sendContent( - EventTypes.CallCandidates, - content, - txid: txid, - ); - } - - /// This event is sent by the callee when they wish to answer the call. - /// [callId] is a unique identifier for the call. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [type] The type of session description. Must be 'answer'. - /// [sdp] The SDP text of the session description. - /// [party_id] The party ID for call, Can be set to client.deviceId. - Future answerCall(String callId, String sdp, String party_id, - {String type = 'answer', - String version = voipProtoVersion, - String? txid, - CallCapabilities? capabilities, - SDPStreamMetadata? metadata}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'answer': {'sdp': sdp, 'type': type}, - if (capabilities != null) 'capabilities': capabilities.toJson(), - if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), - }; - return await _sendContent( - EventTypes.CallAnswer, - content, - txid: txid, - ); - } - - /// This event is sent by the callee when they wish to answer the call. - /// [callId] The ID of the call this event relates to. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - Future hangupCall( - String callId, String party_id, String? hangupCause, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - if (hangupCause != null) 'reason': hangupCause, - }; - return await _sendContent( - EventTypes.CallHangup, - content, - txid: txid, - ); - } - - /// Send SdpStreamMetadata Changed event. - /// - /// This MSC also adds a new call event m.call.sdp_stream_metadata_changed, - /// which has the common VoIP fields as specified in - /// MSC2746 (version, call_id, party_id) and a sdp_stream_metadata object which - /// is the same thing as sdp_stream_metadata in m.call.negotiate, m.call.invite - /// and m.call.answer. The client sends this event the when sdp_stream_metadata - /// has changed but no negotiation is required - /// (e.g. the user mutes their camera/microphone). - /// - /// [callId] The ID of the call this event relates to. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - /// [metadata] The sdp_stream_metadata object. - Future sendSDPStreamMetadataChanged( - String callId, String party_id, SDPStreamMetadata metadata, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - sdpStreamMetadataKey: metadata.toJson(), - }; - return await _sendContent( - EventTypes.CallSDPStreamMetadataChangedPrefix, - content, - txid: txid, - ); - } - - /// CallReplacesEvent for Transfered calls - /// - /// [callId] The ID of the call this event relates to. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - /// [callReplaces] transfer info - Future sendCallReplaces( - String callId, String party_id, CallReplaces callReplaces, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - ...callReplaces.toJson(), - }; - return await _sendContent( - EventTypes.CallReplaces, - content, - txid: txid, - ); - } - - /// send AssertedIdentity event - /// - /// [callId] The ID of the call this event relates to. - /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. - /// [party_id] The party ID for call, Can be set to client.deviceId. - /// [assertedIdentity] the asserted identity - Future sendAssertedIdentity( - String callId, String party_id, AssertedIdentity assertedIdentity, - {String version = voipProtoVersion, String? txid}) async { - txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; - final content = { - 'call_id': callId, - 'party_id': party_id, - 'version': version, - 'asserted_identity': assertedIdentity.toJson(), - }; - return await _sendContent( - EventTypes.CallAssertedIdentity, - content, - txid: txid, - ); - } - /// A room may be public meaning anyone can join the room without any prior action. Alternatively, /// it can be invite meaning that a user who wishes to join the room must first receive an invite /// to the room from someone already inside of the room. Currently, knock and private are reserved diff --git a/lib/src/voip.dart b/lib/src/voip.dart index b86ade56..8a4df9bf 100644 --- a/lib/src/voip.dart +++ b/lib/src/voip.dart @@ -6,6 +6,10 @@ import 'package:sdp_transform/sdp_transform.dart' as sdp_transform; import '../matrix.dart'; +/// https://github.com/matrix-org/matrix-doc/pull/2746 +/// version 1 +const String voipProtoVersion = '1'; + /// Delegate WebRTC basic functionality. abstract class WebRTCDelegate { MediaDevices get mediaDevices; @@ -438,8 +442,8 @@ class CallSession { await pc!.setRemoteDescription(description); if (description.type == 'offer') { final answer = await pc!.createAnswer({}); - await room.sendCallNegotiate( - callId, lifetimeMs, localPartyId, answer.sdp!, + await voip.sendCallNegotiate( + opts.room, callId, lifetimeMs, localPartyId, answer.sdp!, type: answer.type!); await pc!.setLocalDescription(answer); } @@ -728,7 +732,8 @@ class CallSession { video_muted: localUserMediaStream!.stream!.getVideoTracks().isEmpty) }); - final res = await room.answerCall(callId, answer.sdp!, localPartyId, + final res = await voip.sendAnswerCall( + opts.room, callId, answer.sdp!, localPartyId, type: answer.type!, capabilities: callCapabilities, metadata: metadata); @@ -751,7 +756,7 @@ class CallSession { } Logs().d('[VOIP] Rejecting call: $callId'); terminate(CallParty.kLocal, CallErrorCode.UserHangup, true); - room.sendCallReject(callId, lifetimeMs, localPartyId); + voip.sendCallReject(opts.room, callId, lifetimeMs, localPartyId); } void hangup([String? reason, bool suppressEvent = true]) async { @@ -762,7 +767,8 @@ class CallSession { CallParty.kLocal, reason ?? CallErrorCode.UserHangup, !suppressEvent); try { - final res = await room.hangupCall(callId, localPartyId, 'userHangup'); + final res = await voip.sendHangupCall( + opts.room, callId, localPartyId, 'userHangup'); Logs().v('[VOIP] hangup res => $res'); } catch (e) { Logs().v('[VOIP] hangup error => ${e.toString()}'); @@ -842,8 +848,8 @@ class CallSession { ..transferee = false; final metadata = _getLocalSDPStreamMetadata(); if (state == CallState.kCreateOffer) { - await room.inviteToCall( - callId, lifetimeMs, localPartyId, null, offer.sdp!, + await voip.sendInviteToCall( + opts.room, callId, lifetimeMs, localPartyId, null, offer.sdp!, capabilities: callCapabilities, metadata: metadata); inviteOrAnswerSent = true; setCallState(CallState.kInviteSent); @@ -856,7 +862,8 @@ class CallSession { inviteTimer = null; }); } else { - await room.sendCallNegotiate(callId, lifetimeMs, localPartyId, offer.sdp!, + await voip.sendCallNegotiate( + opts.room, callId, lifetimeMs, localPartyId, offer.sdp!, type: offer.type!, capabilities: callCapabilities, metadata: metadata); @@ -948,8 +955,8 @@ class CallSession { _setTracksEnabled(localUserMediaStream?.stream!.getVideoTracks() ?? [], !vidShouldBeMuted); - await opts.room.sendSDPStreamMetadataChanged( - callId, localPartyId, _getLocalSDPStreamMetadata()); + await opts.voip.sendSDPStreamMetadataChanged( + opts.room, callId, localPartyId, _getLocalSDPStreamMetadata()); } void _setTracksEnabled(List tracks, bool enabled) { @@ -1078,8 +1085,8 @@ class CallSession { localCandidates.forEach((element) { candidates.add(element.toMap()); }); - final res = - await room.sendCallCandidates(callId, localPartyId, candidates); + final res = await voip.sendCallCandidates( + opts.room, callId, localPartyId, candidates); Logs().v('[VOIP] sendCallCandidates res => $res'); } catch (e) { Logs().v('[VOIP] sendCallCandidates e => ${e.toString()}'); @@ -1188,7 +1195,7 @@ class VoIP { if (currentCID != null) { // Only one session at a time. Logs().v('[VOIP] onCallInvite: There is already a session.'); - await event.room.hangupCall(callId, localPartyId!, 'userBusy'); + await sendHangupCall(event.room, callId, localPartyId!, 'userBusy'); return; } if (calls[callId] != null) { @@ -1290,8 +1297,8 @@ class VoIP { call.onAnswerReceived(answer, metadata); /// Send select_answer event. - await event.room.selectCallAnswer( - callId, lifetimeMs, localPartyId!, call.remotePartyId!); + await sendSelectCallAnswer( + event.room, callId, lifetimeMs, localPartyId!, call.remotePartyId!); } else { Logs().v('[VOIP] onCallAnswer: Session [$callId] not found!'); } @@ -1505,4 +1512,325 @@ class VoIP { calls[opts.callId] = call; return call; } + + /// This is sent by the caller when they wish to establish a call. + /// [callId] is a unique identifier for the call. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [lifetime] is the time in milliseconds that the invite is valid for. Once the invite age exceeds this value, + /// clients should discard it. They should also no longer show the call as awaiting an answer in the UI. + /// [type] The type of session description. Must be 'offer'. + /// [sdp] The SDP text of the session description. + /// [invitee] The user ID of the person who is being invited. Invites without an invitee field are defined to be + /// intended for any member of the room other than the sender of the event. + /// [party_id] The party ID for call, Can be set to client.deviceId. + Future sendInviteToCall(Room room, String callId, int lifetime, + String party_id, String? invitee, String sdp, + {String type = 'offer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'lifetime': lifetime, + 'offer': {'sdp': sdp, 'type': type}, + if (invitee != null) 'invitee': invitee, + if (capabilities != null) 'capabilities': capabilities.toJson(), + if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallInvite, + content, + txid: txid, + ); + } + + /// The calling party sends the party_id of the first selected answer. + /// + /// Usually after receiving the first answer sdp in the client.onCallAnswer event, + /// save the `party_id`, and then send `CallSelectAnswer` to others peers that the call has been picked up. + /// + /// [callId] is a unique identifier for the call. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + /// [selected_party_id] The party ID for the selected answer. + Future sendSelectCallAnswer(Room room, String callId, int lifetime, + String party_id, String selected_party_id, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'lifetime': lifetime, + 'selected_party_id': selected_party_id, + }; + + return await _sendContent( + room, + EventTypes.CallSelectAnswer, + content, + txid: txid, + ); + } + + /// Reject a call + /// [callId] is a unique identifier for the call. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + Future sendCallReject( + Room room, String callId, int lifetime, String party_id, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'lifetime': lifetime, + }; + + return await _sendContent( + room, + EventTypes.CallReject, + content, + txid: txid, + ); + } + + /// When local audio/video tracks are added/deleted or hold/unhold, + /// need to createOffer and renegotiation. + /// [callId] is a unique identifier for the call. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + Future sendCallNegotiate( + Room room, String callId, int lifetime, String party_id, String sdp, + {String type = 'offer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'lifetime': lifetime, + 'description': {'sdp': sdp, 'type': type}, + if (capabilities != null) 'capabilities': capabilities.toJson(), + if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallNegotiate, + content, + txid: txid, + ); + } + + /// This is sent by callers after sending an invite and by the callee after answering. + /// Its purpose is to give the other party additional ICE candidates to try using to communicate. + /// + /// [callId] The ID of the call this event relates to. + /// + /// [version] The version of the VoIP specification this messages adheres to. This specification is version 1. + /// + /// [party_id] The party ID for call, Can be set to client.deviceId. + /// + /// [candidates] Array of objects describing the candidates. Example: + /// + /// ``` + /// [ + /// { + /// "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0", + /// "sdpMLineIndex": 0, + /// "sdpMid": "audio" + /// } + /// ], + /// ``` + Future sendCallCandidates( + Room room, + String callId, + String party_id, + List> candidates, { + String version = voipProtoVersion, + String? txid, + }) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'candidates': candidates, + }; + return await _sendContent( + room, + EventTypes.CallCandidates, + content, + txid: txid, + ); + } + + /// This event is sent by the callee when they wish to answer the call. + /// [callId] is a unique identifier for the call. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [type] The type of session description. Must be 'answer'. + /// [sdp] The SDP text of the session description. + /// [party_id] The party ID for call, Can be set to client.deviceId. + Future sendAnswerCall( + Room room, String callId, String sdp, String party_id, + {String type = 'answer', + String version = voipProtoVersion, + String? txid, + CallCapabilities? capabilities, + SDPStreamMetadata? metadata}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'answer': {'sdp': sdp, 'type': type}, + if (capabilities != null) 'capabilities': capabilities.toJson(), + if (metadata != null) sdpStreamMetadataKey: metadata.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallAnswer, + content, + txid: txid, + ); + } + + /// This event is sent by the callee when they wish to answer the call. + /// [callId] The ID of the call this event relates to. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + Future sendHangupCall( + Room room, String callId, String party_id, String? hangupCause, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + if (hangupCause != null) 'reason': hangupCause, + }; + return await _sendContent( + room, + EventTypes.CallHangup, + content, + txid: txid, + ); + } + + /// Send SdpStreamMetadata Changed event. + /// + /// This MSC also adds a new call event m.call.sdp_stream_metadata_changed, + /// which has the common VoIP fields as specified in + /// MSC2746 (version, call_id, party_id) and a sdp_stream_metadata object which + /// is the same thing as sdp_stream_metadata in m.call.negotiate, m.call.invite + /// and m.call.answer. The client sends this event the when sdp_stream_metadata + /// has changed but no negotiation is required + /// (e.g. the user mutes their camera/microphone). + /// + /// [callId] The ID of the call this event relates to. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + /// [metadata] The sdp_stream_metadata object. + Future sendSDPStreamMetadataChanged( + Room room, String callId, String party_id, SDPStreamMetadata metadata, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + sdpStreamMetadataKey: metadata.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallSDPStreamMetadataChangedPrefix, + content, + txid: txid, + ); + } + + /// CallReplacesEvent for Transfered calls + /// + /// [callId] The ID of the call this event relates to. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + /// [callReplaces] transfer info + Future sendCallReplaces( + Room room, String callId, String party_id, CallReplaces callReplaces, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + ...callReplaces.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallReplaces, + content, + txid: txid, + ); + } + + /// send AssertedIdentity event + /// + /// [callId] The ID of the call this event relates to. + /// [version] is the version of the VoIP specification this message adheres to. This specification is version 1. + /// [party_id] The party ID for call, Can be set to client.deviceId. + /// [assertedIdentity] the asserted identity + Future sendAssertedIdentity(Room room, String callId, + String party_id, AssertedIdentity assertedIdentity, + {String version = voipProtoVersion, String? txid}) async { + txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; + final content = { + 'call_id': callId, + 'party_id': party_id, + 'version': version, + 'asserted_identity': assertedIdentity.toJson(), + }; + return await _sendContent( + room, + EventTypes.CallAssertedIdentity, + content, + txid: txid, + ); + } + + Future _sendContent( + Room room, + String type, + Map content, { + String? txid, + }) async { + txid ??= client.generateUniqueTransactionId(); + + final mustEncrypt = room.encrypted && client.encryptionEnabled; + + final sendMessageContent = mustEncrypt + ? await client.encryption! + .encryptGroupMessagePayload(room.id, content, type: type) + : content; + return await client.sendMessage( + room.id, + sendMessageContent.containsKey('ciphertext') + ? EventTypes.Encrypted + : type, + txid, + sendMessageContent, + ); + } } diff --git a/test/fake_voip_delegate.dart b/test/fake_voip_delegate.dart new file mode 100644 index 00000000..4e0ade42 --- /dev/null +++ b/test/fake_voip_delegate.dart @@ -0,0 +1,52 @@ +import 'package:matrix/src/voip.dart'; +import 'package:webrtc_interface/src/rtc_video_renderer.dart'; +import 'package:webrtc_interface/src/rtc_peerconnection.dart'; +import 'package:webrtc_interface/src/mediadevices.dart'; + +class FakeVoIPDelegate extends WebRTCDelegate { + @override + Future createPeerConnection( + Map configuration, + [Map constraints = const {}]) { + // TODO: implement createPeerConnection + throw UnimplementedError(); + } + + @override + VideoRenderer createRenderer() { + // TODO: implement createRenderer + throw UnimplementedError(); + } + + @override + void handleCallEnded(CallSession session) { + // TODO: implement handleCallEnded + } + + @override + void handleNewCall(CallSession session) { + // TODO: implement handleNewCall + } + + @override + // TODO: implement isBackgroud + bool get isBackgroud => throw UnimplementedError(); + + @override + // TODO: implement isWeb + bool get isWeb => throw UnimplementedError(); + + @override + // TODO: implement mediaDevices + MediaDevices get mediaDevices => throw UnimplementedError(); + + @override + void playRingtone() { + // TODO: implement playRingtone + } + + @override + void stopRingtone() { + // TODO: implement stopRingtone + } +} diff --git a/test/room_test.dart b/test/room_test.dart index c47ae44d..56894bfb 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -25,16 +25,19 @@ import 'package:test/test.dart'; import 'fake_client.dart'; import 'fake_matrix_api.dart'; +import 'fake_voip_delegate.dart'; void main() { late Client matrix; late Room room; + late VoIP voip; /// All Tests related to the Event group('Room', () { Logs().level = Level.error; test('Login', () async { matrix = await getClient(); + voip = VoIP(matrix, FakeVoIPDelegate()); }); test('Create from json', () async { @@ -753,24 +756,29 @@ void main() { }); test('Test call methods', () async { - await room.inviteToCall('1234', 1234, '4567', '7890', 'sdp', + await voip.sendInviteToCall(room, '1234', 1234, '4567', '7890', 'sdp', txid: '1234'); - await room.answerCall('1234', 'sdp', '4567', txid: '1234'); - await room.sendCallCandidates('1234', '4567', [], txid: '1234'); - await room.selectCallAnswer('1234', 1234, '4567', '6789', txid: '1234'); - await room.sendCallReject('1234', 1234, '4567', txid: '1234'); - await room.sendCallNegotiate('1234', 1234, '4567', 'sdp', txid: '1234'); - await room.hangupCall('1234', '4567', 'user_hangup', txid: '1234'); - await room.sendAssertedIdentity( + await voip.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234'); + await voip.sendCallCandidates(room, '1234', '4567', [], txid: '1234'); + await voip.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789', + txid: '1234'); + await voip.sendCallReject(room, '1234', 1234, '4567', txid: '1234'); + await voip.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp', + txid: '1234'); + await voip.sendHangupCall(room, '1234', '4567', 'user_hangup', + txid: '1234'); + await voip.sendAssertedIdentity( + room, '1234', '4567', AssertedIdentity() ..displayName = 'name' ..id = 'some_id', txid: '1234'); - await room.sendCallReplaces('1234', '4567', CallReplaces(), txid: '1234'); - await room.sendSDPStreamMetadataChanged( - '1234', '4567', SDPStreamMetadata({}), + await voip.sendCallReplaces(room, '1234', '4567', CallReplaces(), + txid: '1234'); + await voip.sendSDPStreamMetadataChanged( + room, '1234', '4567', SDPStreamMetadata({}), txid: '1234'); });