fix: ignore calls with age older than lifetime

feat: add barebones WebRTCDelegateMock
This commit is contained in:
td 2023-12-18 21:28:26 +05:30
parent 520dfdbe3e
commit 6a2f31de39
No known key found for this signature in database
GPG Key ID: 62A30523D4D6CE28
6 changed files with 995 additions and 40 deletions

View File

@ -2142,6 +2142,22 @@ class Client extends MatrixApi {
return false;
});
}
final age = callEvent.unsigned?.tryGet<int>('age') ??
(DateTime.now().millisecondsSinceEpoch -
callEvent.originServerTs.millisecondsSinceEpoch);
callEvents.removeWhere((element) {
if (callEvent.type == EventTypes.CallInvite &&
age >
(callEvent.content.tryGet<int>('lifetime') ??
CallTimeouts.callInviteLifetime.inMilliseconds)) {
Logs().v(
'Ommiting invite event ${callEvent.eventId} as age was older than lifetime');
return true;
}
return false;
});
}
}
}

View File

@ -1086,7 +1086,7 @@ class CallSession {
return callOnHold;
}
Future<void> answer() async {
Future<void> answer({String? txid}) async {
if (inviteOrAnswerSent) {
return;
}
@ -1124,10 +1124,16 @@ class CallSession {
// Allow a short time for initial candidates to be gathered
await Future.delayed(Duration(milliseconds: 200));
final res = await sendAnswerCall(room, callId, answer.sdp!, localPartyId,
type: answer.type!,
capabilities: callCapabilities,
metadata: metadata);
final res = await sendAnswerCall(
room,
callId,
answer.sdp!,
localPartyId,
type: answer.type!,
capabilities: callCapabilities,
metadata: metadata,
txid: txid,
);
Logs().v('[VOIP] answer res => $res');
inviteOrAnswerSent = true;

View File

@ -267,12 +267,9 @@ class VoIP {
// initWithInvite, we might set it to callId even after it was reset to null
// by terminate.
currentCID = callId;
try {
await newCall.initWithInvite(
callType, offer, sdpStreamMetadata, lifetime, confId != null);
} catch (e, s) {
Logs().e('[VOIP] initWithInvite failed', e, s);
}
await newCall.initWithInvite(
callType, offer, sdpStreamMetadata, lifetime, confId != null);
// Popup CallingPage for incoming call.
if (confId == null && !newCall.callHasEnded) {

141
test/calls_test.dart Normal file
View File

@ -0,0 +1,141 @@
import 'package:test/test.dart';
import 'package:matrix/matrix.dart';
import 'fake_client.dart';
import 'webrtc_stub.dart';
void main() {
late Client matrix;
late Room room;
group('Call Tests', () {
Logs().level = Level.info;
test('Login', () async {
matrix = await getClient();
});
test('Create from json', () async {
final id = '!localpart:server.abc';
final membership = Membership.join;
room = Room(
client: matrix,
id: id,
membership: membership,
prev_batch: '',
);
});
test('Test call methods', () async {
final call = CallSession(CallOptions()..room = room);
await call.sendInviteToCall(room, '1234', 1234, '4567', '7890', 'sdp',
txid: '1234');
await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234');
await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
await call.sendSelectCallAnswer(room, '1234', '4567', '6789',
txid: '1234');
await call.sendCallReject(room, '1234', '4567', 'busy', txid: '1234');
await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
txid: '1234');
await call.sendHangupCall(room, '1234', '4567', 'user_hangup',
txid: '1234');
await call.sendAssertedIdentity(
room,
'1234',
'4567',
AssertedIdentity()
..displayName = 'name'
..id = 'some_id',
txid: '1234');
await call.sendCallReplaces(room, '1234', '4567', CallReplaces(),
txid: '1234');
await call.sendSDPStreamMetadataChanged(
room, '1234', '4567', SDPStreamMetadata({}),
txid: '1234');
});
test('Test call lifetime', () async {
final voip = VoIP(matrix, MockWebRTCDelegate());
expect(voip.currentCID, null);
// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': '1702472924955oq1uQbNAfU7wAaEA',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.utc(1969),
)
]))
})));
await Future.delayed(Duration(seconds: 3));
// confirm that no call got created after 3 seconds, which is
// expected in this case because the originTs was old asf
expect(voip.currentCID, null);
// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
unsigned: {'age': 60001},
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'unsignedTsInvalidCall',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.now(),
)
]))
})));
await Future.delayed(Duration(seconds: 3));
// confirm that no call got created after 3 seconds, which is
// expected in this case because age was older than lifetime
expect(voip.currentCID, null);
// persist normal room messages
await matrix.handleSync(SyncUpdate(
nextBatch: 'something',
rooms: RoomsUpdate(join: {
room.id: JoinedRoomUpdate(
timeline: TimelineUpdate(events: [
MatrixEvent(
type: 'm.call.invite',
content: {
'lifetime': 60000,
'call_id': 'originTsValidCall',
'party_id': 'DPCIPPBGPO',
'offer': {'type': 'offer', 'sdp': 'sdp'}
},
senderId: '@alice:testing.com',
eventId: 'newevent',
originServerTs: DateTime.now(),
)
]))
})));
while (voip.currentCID != 'originTsValidCall') {
// call invite looks valid, call should be created now :D
await Future.delayed(Duration(milliseconds: 50));
Logs().d('Waiting for currentCID to update');
}
expect(voip.currentCID, 'originTsValidCall');
final call = voip.calls[voip.currentCID]!;
await call.answer(txid: '1234');
expect(call.state, CallState.kConnecting);
});
});
}

View File

@ -1052,35 +1052,6 @@ void main() {
expect(room.pushRuleState, PushRuleState.dontNotify);
});
test('Test call methods', () async {
final call = CallSession(CallOptions()..room = room);
await call.sendInviteToCall(room, '1234', 1234, '4567', '7890', 'sdp',
txid: '1234');
await call.sendAnswerCall(room, '1234', 'sdp', '4567', txid: '1234');
await call.sendCallCandidates(room, '1234', '4567', [], txid: '1234');
await call.sendSelectCallAnswer(room, '1234', 1234, '4567', '6789',
txid: '1234');
await call.sendCallReject(room, '1234', 1234, '4567', 'busy',
txid: '1234');
await call.sendCallNegotiate(room, '1234', 1234, '4567', 'sdp',
txid: '1234');
await call.sendHangupCall(room, '1234', '4567', 'user_hangup',
txid: '1234');
await call.sendAssertedIdentity(
room,
'1234',
'4567',
AssertedIdentity()
..displayName = 'name'
..id = 'some_id',
txid: '1234');
await call.sendCallReplaces(room, '1234', '4567', CallReplaces(),
txid: '1234');
await call.sendSDPStreamMetadataChanged(
room, '1234', '4567', SDPStreamMetadata({}),
txid: '1234');
});
test('enableEncryption', () async {
await room.enableEncryption();
});

824
test/webrtc_stub.dart Normal file
View File

@ -0,0 +1,824 @@
import 'dart:typed_data';
import 'package:webrtc_interface/webrtc_interface.dart';
import 'package:matrix/matrix.dart';
class MockWebRTCDelegate implements WebRTCDelegate {
@override
// TODO: implement canHandleNewCall
bool get canHandleNewCall => true;
@override
Future<RTCPeerConnection> createPeerConnection(
Map<String, dynamic> configuration, [
Map<String, dynamic> constraints = const {},
]) async =>
MockRTCPeerConnection();
@override
VideoRenderer createRenderer() {
return MockVideoRenderer();
}
@override
Future<void> handleCallEnded(CallSession session) async {
Logs().i('handleCallEnded called in MockWebRTCDelegate');
}
@override
Future<void> handleGroupCallEnded(GroupCall groupCall) async {
Logs().i('handleGroupCallEnded called in MockWebRTCDelegate');
}
@override
Future<void> handleMissedCall(CallSession session) async {
Logs().i('handleMissedCall called in MockWebRTCDelegate');
}
@override
Future<void> handleNewCall(CallSession session) async {
Logs().i('handleNewCall called in MockWebRTCDelegate');
}
@override
Future<void> handleNewGroupCall(GroupCall groupCall) async {
Logs().i('handleNewGroupCall called in MockWebRTCDelegate');
}
@override
bool get isWeb => false;
@override
MediaDevices get mediaDevices => MockMediaDevices();
@override
Future<void> playRingtone() async {
Logs().i('playRingtone called in MockWebRTCDelegate');
}
@override
Future<void> stopRingtone() async {
Logs().i('stopRingtone called in MockWebRTCDelegate');
}
}
class MockMediaDevices implements MediaDevices {
@override
Function(dynamic event)? ondevicechange;
@override
Future<List<MediaDeviceInfo>> enumerateDevices() {
// TODO: implement enumerateDevices
throw UnimplementedError();
}
@override
Future<MediaStream> getDisplayMedia(Map<String, dynamic> mediaConstraints) {
// TODO: implement getDisplayMedia
throw UnimplementedError();
}
@override
Future<List> getSources() {
// TODO: implement getSources
throw UnimplementedError();
}
@override
MediaTrackSupportedConstraints getSupportedConstraints() {
// TODO: implement getSupportedConstraints
throw UnimplementedError();
}
@override
Future<MediaStream> getUserMedia(
Map<String, dynamic> mediaConstraints) async {
return MockMediaStream('', '');
}
@override
Future<MediaDeviceInfo> selectAudioOutput([AudioOutputOptions? options]) {
// TODO: implement selectAudioOutput
throw UnimplementedError();
}
}
class MockRTCPeerConnection implements RTCPeerConnection {
@override
Function(RTCSignalingState state)? onSignalingState;
@override
Function(RTCPeerConnectionState state)? onConnectionState;
@override
Function(RTCIceGatheringState state)? onIceGatheringState;
@override
Function(RTCIceConnectionState state)? onIceConnectionState;
@override
Function(RTCIceCandidate candidate)? onIceCandidate;
@override
Function(MediaStream stream)? onAddStream;
@override
Function(MediaStream stream)? onRemoveStream;
@override
Function(MediaStream stream, MediaStreamTrack track)? onAddTrack;
@override
Function(MediaStream stream, MediaStreamTrack track)? onRemoveTrack;
@override
Function(RTCDataChannel channel)? onDataChannel;
@override
Function()? onRenegotiationNeeded;
@override
Function(RTCTrackEvent event)? onTrack;
@override
RTCSignalingState? get signalingState => throw UnimplementedError();
@override
Future<RTCSignalingState?> getSignalingState() async {
return signalingState;
}
@override
RTCIceGatheringState? get iceGatheringState => throw UnimplementedError();
@override
Future<RTCIceGatheringState?> getIceGatheringState() async {
return iceGatheringState;
}
@override
RTCIceConnectionState? get iceConnectionState => throw UnimplementedError();
@override
Future<RTCIceConnectionState?> getIceConnectionState() async {
return iceConnectionState;
}
@override
RTCPeerConnectionState? get connectionState => throw UnimplementedError();
@override
Future<RTCPeerConnectionState?> getConnectionState() async {
return connectionState;
}
@override
Future<void> dispose() async {
// Mock implementation for disposing the connection
Logs().i('Mock: Disposing RTCPeerConnection');
}
@override
Map<String, dynamic> get getConfiguration => throw UnimplementedError();
@override
Future<void> setConfiguration(Map<String, dynamic> configuration) async {
// Mock implementation for setting configuration
Logs().i('Mock: Setting RTCPeerConnection configuration');
}
@override
Future<RTCSessionDescription> createOffer(
[Map<String, dynamic>? constraints]) {
// Mock implementation for creating an offer
Logs().i('Mock: Creating offer');
return Future.value(RTCSessionDescription('', ''));
}
@override
Future<RTCSessionDescription> createAnswer(
[Map<String, dynamic>? constraints]) {
// Mock implementation for creating an answer
Logs().i('Mock: Creating answer');
return Future.value(RTCSessionDescription('', ''));
}
@override
Future<void> addStream(MediaStream stream) async {
// Mock implementation for adding a stream
Logs().i('Mock: Adding stream');
}
@override
Future<void> removeStream(MediaStream stream) async {
// Mock implementation for removing a stream
Logs().i('Mock: Removing stream');
}
@override
Future<RTCSessionDescription?> getLocalDescription() async {
// Mock implementation for getting local description
Logs().i('Mock: Getting local description');
return RTCSessionDescription('', '');
}
@override
Future<void> setLocalDescription(RTCSessionDescription description) async {
// Mock implementation for setting local description
Logs().i('Mock: Setting local description');
}
@override
Future<RTCSessionDescription?> getRemoteDescription() async {
// Mock implementation for getting remote description
Logs().i('Mock: Getting remote description');
return RTCSessionDescription('', '');
}
@override
Future<void> setRemoteDescription(RTCSessionDescription description) async {
// Mock implementation for setting remote description
Logs().i('Mock: Setting remote description');
}
@override
Future<void> addCandidate(RTCIceCandidate candidate) async {
// Mock implementation for adding a candidate
Logs().i('Mock: Adding ICE candidate');
}
@override
Future<List<StatsReport>> getStats([MediaStreamTrack? track]) async {
// Mock implementation for getting stats
Logs().i('Mock: Getting stats');
return [];
}
@override
List<MediaStream?> getLocalStreams() {
// Mock implementation for getting local streams
Logs().i('Mock: Getting local streams');
return [];
}
@override
List<MediaStream?> getRemoteStreams() {
// Mock implementation for getting remote streams
Logs().i('Mock: Getting remote streams');
return [];
}
@override
Future<RTCDataChannel> createDataChannel(
String label, RTCDataChannelInit dataChannelDict) async {
// Mock implementation for creating a data channel
Logs().i('Mock: Creating data channel');
return MockRTCDataChannel();
}
@override
Future<void> restartIce() async {
// Mock implementation for restarting ICE
Logs().i('Mock: Restarting ICE');
}
@override
Future<void> close() async {
// Mock implementation for closing the connection
Logs().i('Mock: Closing RTCPeerConnection');
}
@override
RTCDTMFSender createDtmfSender(MediaStreamTrack track) {
// Mock implementation for creating a DTMF sender
Logs().i('Mock: Creating DTMF sender');
return MockRTCDTMFSender();
}
@override
Future<List<RTCRtpSender>> getSenders() async {
// Mock implementation for getting senders
Logs().i('Mock: Getting senders');
return [];
}
@override
Future<List<RTCRtpReceiver>> getReceivers() async {
// Mock implementation for getting receivers
Logs().i('Mock: Getting receivers');
return [];
}
@override
Future<List<RTCRtpTransceiver>> getTransceivers() async {
// Mock implementation for getting transceivers
Logs().i('Mock: Getting transceivers');
return [];
}
@override
Future<RTCRtpSender> addTrack(MediaStreamTrack track,
[MediaStream? stream]) async {
// Mock implementation for adding a track
Logs().i('Mock: Adding track');
return MockRTCRtpSender();
}
@override
Future<bool> removeTrack(RTCRtpSender sender) async {
// Mock implementation for removing a track
Logs().i('Mock: Removing track');
return true;
}
@override
Future<RTCRtpTransceiver> addTransceiver(
{MediaStreamTrack? track,
RTCRtpMediaType? kind,
RTCRtpTransceiverInit? init}) async {
// Mock implementation for adding a transceiver
Logs().i('Mock: Adding transceiver');
return MockRTCRtpTransceiver();
}
@override
// TODO: implement receivers
Future<List<RTCRtpReceiver>> get receivers => throw UnimplementedError();
@override
// TODO: implement senders
Future<List<RTCRtpSender>> get senders => throw UnimplementedError();
@override
// TODO: implement transceivers
Future<List<RTCRtpTransceiver>> get transceivers =>
throw UnimplementedError();
}
class MockRTCRtpTransceiver implements RTCRtpTransceiver {
@override
Future<TransceiverDirection?> getCurrentDirection() async {
// Mock implementation for getting current direction
Logs().i('Mock: Getting current direction');
return TransceiverDirection.SendRecv;
}
@override
Future<void> setDirection(TransceiverDirection direction) async {
// Mock implementation for setting direction
Logs().i('Mock: Setting direction');
}
@override
Future<TransceiverDirection> getDirection() async {
// Mock implementation for getting direction
Logs().i('Mock: Getting direction');
return TransceiverDirection.SendRecv;
}
@override
Future<void> setCodecPreferences(List<RTCRtpCodecCapability> codecs) async {
// Mock implementation for setting codec preferences
Logs().i('Mock: Setting codec preferences');
}
@override
String get mid => 'mock_mid';
@override
RTCRtpSender get sender => MockRTCRtpSender();
@override
RTCRtpReceiver get receiver => MockRTCRtpReceiver();
bool get stopped => false;
@override
String get transceiverId => 'mock_transceiver_id';
@override
Future<void> stop() async {
// Mock implementation for stopping transceiver
Logs().i('Mock: Stopping transceiver');
}
@override
TransceiverDirection get currentDirection {
// Deprecated method, should be replaced with `await getCurrentDirection`
throw UnimplementedError(
'Need to be call asynchronously from native SDK, so the method is deprecated');
}
@override
// TODO: implement stoped
bool get stoped => throw UnimplementedError();
}
class MockRTCRtpSender implements RTCRtpSender {
@override
Future<void> dispose() {
// TODO: implement dispose
throw UnimplementedError();
}
@override
// TODO: implement dtmfSender
RTCDTMFSender get dtmfSender => throw UnimplementedError();
@override
Future<List<StatsReport>> getStats() {
// TODO: implement getStats
throw UnimplementedError();
}
@override
// TODO: implement ownsTrack
bool get ownsTrack => throw UnimplementedError();
@override
// TODO: implement parameters
RTCRtpParameters get parameters => throw UnimplementedError();
@override
Future<void> replaceTrack(MediaStreamTrack? track) {
// TODO: implement replaceTrack
throw UnimplementedError();
}
@override
// TODO: implement senderId
String get senderId => throw UnimplementedError();
@override
Future<bool> setParameters(RTCRtpParameters parameters) {
// TODO: implement setParameters
throw UnimplementedError();
}
@override
Future<void> setStreams(List<MediaStream> streams) {
// TODO: implement setStreams
throw UnimplementedError();
}
@override
Future<void> setTrack(MediaStreamTrack? track, {bool takeOwnership = true}) {
// TODO: implement setTrack
throw UnimplementedError();
}
@override
// TODO: implement track
MediaStreamTrack? get track => throw UnimplementedError();
// Mock implementation for RTCRtpSender
}
class MockRTCRtpReceiver implements RTCRtpReceiver {
@override
Function(RTCRtpReceiver rtpReceiver, RTCRtpMediaType mediaType)?
onFirstPacketReceived;
@override
Future<List<StatsReport>> getStats() {
// TODO: implement getStats
throw UnimplementedError();
}
@override
// TODO: implement parameters
RTCRtpParameters get parameters => throw UnimplementedError();
@override
// TODO: implement receiverId
String get receiverId => throw UnimplementedError();
@override
// TODO: implement track
MediaStreamTrack? get track => throw UnimplementedError();
// Mock implementation for RTCRtpReceiver
}
typedef StreamTrackCallback = void Function();
class MockMediaStreamTrack implements MediaStreamTrack {
@override
String? get id => 'mock_id';
@override
String? get label => 'mock_label';
@override
String? get kind => 'mock_kind';
@override
StreamTrackCallback? onMute;
@override
StreamTrackCallback? onUnMute;
@override
StreamTrackCallback? onEnded;
@override
bool get enabled => true;
@override
set enabled(bool b) {
// Mock implementation for setting enable state
Logs().i('Mock: Setting MediaStreamTrack enable state');
}
@override
bool? get muted => false;
@override
Map<String, dynamic> getConstraints() {
throw UnimplementedError();
}
@override
Future<void> applyConstraints([Map<String, dynamic>? constraints]) async {
throw UnimplementedError();
}
@override
Future<MediaStreamTrack> clone() async {
throw UnimplementedError();
}
@override
Future<void> stop() async {
// Mock implementation for stopping the track
Logs().i('Mock: Stopping MediaStreamTrack');
}
@override
Map<String, dynamic> getSettings() {
throw UnimplementedError();
}
@override
Future<bool> switchCamera() async {
throw UnimplementedError();
}
@override
Future<void> adaptRes(int width, int height) async {
throw UnimplementedError();
}
@override
void enableSpeakerphone(bool enable) {
throw UnimplementedError();
}
@override
Future<ByteBuffer> captureFrame() async {
throw UnimplementedError();
}
@override
Future<bool> hasTorch() async {
throw UnimplementedError();
}
@override
Future<void> setTorch(bool torch) async {
throw UnimplementedError();
}
@override
@Deprecated('use stop() instead')
Future<void> dispose() async {
// Mock implementation for disposing the track
Logs().i('Mock: Disposing MediaStreamTrack');
}
@override
String toString() {
return 'Track(id: $id, kind: $kind, label: $label, enabled: $enabled, muted: $muted)';
}
}
class MockRTCDTMFSender implements RTCDTMFSender {
@override
Future<void> insertDTMF(String tones,
{int duration = 100, int interToneGap = 70}) async {
// Mock implementation for inserting DTMF tones
Logs().i(
'Mock: Inserting DTMF tones: $tones, Duration: $duration, InterToneGap: $interToneGap');
}
@override
@Deprecated('Use method insertDTMF instead')
Future<void> sendDtmf(String tones,
{int duration = 100, int interToneGap = 70}) async {
return insertDTMF(tones, duration: duration, interToneGap: interToneGap);
}
@override
Future<bool> canInsertDtmf() async {
// Mock implementation for checking if DTMF can be inserted
Logs().i('Mock: Checking if DTMF can be inserted');
return true;
}
}
class MockRTCDataChannel implements RTCDataChannel {
@override
Function(RTCDataChannelState state)? onDataChannelState;
@override
Function(RTCDataChannelMessage data)? onMessage;
@override
Function(int currentAmount, int changedAmount)? onBufferedAmountChange;
@override
Function(int currentAmount)? onBufferedAmountLow;
@override
RTCDataChannelState? get state => RTCDataChannelState.RTCDataChannelOpen;
@override
int? get id => 1;
@override
String? get label => 'mock_label';
@override
int? get bufferedAmount => 0;
@override
int? bufferedAmountLowThreshold;
@override
late Stream<RTCDataChannelState> stateChangeStream;
@override
late Stream<RTCDataChannelMessage> messageStream;
@override
Future<void> send(RTCDataChannelMessage message) async {
// Mock implementation for sending a message
Logs().i('Mock: Sending RTCDataChannelMessage: $message');
}
@override
Future<void> close() async {
// Mock implementation for closing the data channel
Logs().i('Mock: Closing RTCDataChannel');
}
}
class MockMediaStream implements MediaStream {
final String _id;
final String _ownerTag;
bool _isActive = true; // Initially set as active
MockMediaStream(this._id, this._ownerTag);
@override
Function(MediaStreamTrack track)? onAddTrack;
@override
Function(MediaStreamTrack track)? onRemoveTrack;
@override
String get id => _id;
@override
String get ownerTag => _ownerTag;
@override
bool? get active => _isActive;
@override
Future<void> getMediaTracks() async {
// Mock implementation for getting media tracks
Logs().i('Mock: Getting media tracks');
}
@override
Future<void> addTrack(MediaStreamTrack track,
{bool addToNative = true}) async {
// Mock implementation for adding a track
Logs().i('Mock: Adding track to MediaStream: $track');
onAddTrack?.call(track);
}
@override
Future<void> removeTrack(MediaStreamTrack track,
{bool removeFromNative = true}) async {
// Mock implementation for removing a track
Logs().i('Mock: Removing track from MediaStream: $track');
onRemoveTrack?.call(track);
}
@override
List<MediaStreamTrack> getTracks() {
// Mock implementation for getting all tracks
Logs().i('Mock: Getting all tracks');
return [];
}
@override
List<MediaStreamTrack> getAudioTracks() {
// Mock implementation for getting audio tracks
Logs().i('Mock: Getting audio tracks');
return [];
}
@override
List<MediaStreamTrack> getVideoTracks() {
// Mock implementation for getting video tracks
Logs().i('Mock: Getting video tracks');
return [];
}
@override
MediaStreamTrack? getTrackById(String trackId) {
// Mock implementation for getting a track by ID
Logs().i('Mock: Getting track by ID: $trackId');
return null;
}
@override
Future<MediaStream> clone() async {
// Mock implementation for cloning the media stream
Logs().i('Mock: Cloning MediaStream');
return MockMediaStream('${_id}_clone', _ownerTag);
}
@override
Future<void> dispose() async {
// Mock implementation for disposing the media stream
Logs().i('Mock: Disposing MediaStream');
_isActive = false;
}
}
class MockVideoRenderer implements VideoRenderer {
@override
Function? onResize;
@override
Function? onFirstFrameRendered;
final int _videoWidth = 0;
final int _videoHeight = 0;
bool _muted = false;
final bool _renderVideo = true;
int? _textureId;
MediaStream? _srcObject;
@override
int get videoWidth => _videoWidth;
@override
int get videoHeight => _videoHeight;
@override
bool get muted => _muted;
@override
set muted(bool mute) {
_muted = mute;
// Mock implementation for muting/unmuting
Logs().i('Mock: Setting mute state: $_muted');
}
@override
Future<bool> audioOutput(String deviceId) async {
// Mock implementation for changing audio output
Logs().i('Mock: Changing audio output to device ID: $deviceId');
return true; // Mocking successful audio output change
}
@override
bool get renderVideo => _renderVideo;
@override
int? get textureId => _textureId;
@override
Future<void> initialize() async {
// Mock implementation for initialization
Logs().i('Mock: Initializing VideoRenderer');
}
@override
MediaStream? get srcObject => _srcObject;
@override
set srcObject(MediaStream? stream) {
_srcObject = stream;
// Mock implementation for setting source object
Logs().i('Mock: Setting source object for VideoRenderer');
}
@override
Future<void> dispose() async {
// Mock implementation for disposing VideoRenderer
Logs().i('Mock: Disposing VideoRenderer');
}
}