Skip to content

Commit

Permalink
Merge branch 'feat/voip-connection-tester' into 'main'
Browse files Browse the repository at this point in the history
chore: Add voip connection tester.

See merge request famedly/company/frontend/famedlysdk!1142
  • Loading branch information
td-famedly committed Dec 1, 2022
2 parents 15c643b + 0bd8848 commit 90e8d5b
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/matrix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export 'src/voip/call.dart';
export 'src/voip/group_call.dart';
export 'src/voip/voip.dart';
export 'src/voip/voip_content.dart';
export 'src/voip/conn_tester.dart';
export 'src/voip/utils.dart';
export 'src/room.dart';
export 'src/timeline.dart';
Expand Down
113 changes: 113 additions & 0 deletions lib/src/voip/conn_tester.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import 'dart:async';

import 'package:webrtc_interface/webrtc_interface.dart';

import 'package:matrix/matrix.dart';

class ConnectionTester {
Client client;
WebRTCDelegate delegate;
RTCPeerConnection? pc1, pc2;
ConnectionTester(this.client, this.delegate);
TurnServerCredentials? _turnServerCredentials;

Future<bool> verifyTurnServer() async {
final iceServers = await getIceSevers();
final configuration = <String, dynamic>{
'iceServers': iceServers,
'sdpSemantics': 'unified-plan',
'iceCandidatePoolSize': 1,
'iceTransportPolicy': 'relay'
};
pc1 = await delegate.createPeerConnection(configuration);
pc2 = await delegate.createPeerConnection(configuration);

pc1!.onIceCandidate = (candidate) {
if (candidate.candidate!.contains('relay')) {
pc2!.addCandidate(candidate);
}
};
pc2!.onIceCandidate = (candidate) {
if (candidate.candidate!.contains('relay')) {
pc1!.addCandidate(candidate);
}
};

await pc1!.createDataChannel('conn-tester', RTCDataChannelInit());

final offer = await pc1!.createOffer();

await pc2!.setRemoteDescription(offer);
final answer = await pc2!.createAnswer();

await pc1!.setLocalDescription(offer);
await pc2!.setLocalDescription(answer);

await pc1!.setRemoteDescription(answer);

void dispose() {
pc1!.close();
pc1!.dispose();
pc2!.close();
pc2!.dispose();
}

bool connected = false;
try {
await waitUntilAsync(() async {
if (pc1!.connectionState ==
RTCPeerConnectionState.RTCPeerConnectionStateConnected &&
pc2!.connectionState ==
RTCPeerConnectionState.RTCPeerConnectionStateConnected) {
connected = true;
return true;
}
return false;
});
} catch (e) {
Logs().e('[VOIP] ConnectionTester Error while testing TURN server: $e');
}

dispose();
return connected;
}

Future<int> waitUntilAsync(Future<bool> Function() test,
{final int maxIterations = 1000,
final Duration step = const Duration(milliseconds: 10)}) async {
int iterations = 0;
for (; iterations < maxIterations; iterations++) {
await Future.delayed(step);
if (await test()) {
break;
}
}
if (iterations >= maxIterations) {
throw TimeoutException(
'Condition not reached within ${iterations * step.inMilliseconds}ms');
}
return iterations;
}

Future<List<Map<String, dynamic>>> getIceSevers() async {
if (_turnServerCredentials == null) {
try {
_turnServerCredentials = await client.getTurnServer();
} catch (e) {
Logs().v('[VOIP] getTurnServerCredentials error => ${e.toString()}');
}
}

if (_turnServerCredentials == null) {
return [];
}

return [
{
'username': _turnServerCredentials!.username,
'credential': _turnServerCredentials!.password,
'url': _turnServerCredentials!.uris[0]
}
];
}
}

0 comments on commit 90e8d5b

Please sign in to comment.