123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Dart
		
	
	
	
			
		
		
	
	
			123 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Dart
		
	
	
	
| 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 getIceServers();
 | |
|     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')) {
 | |
|         unawaited(pc2!.addCandidate(candidate));
 | |
|       }
 | |
|     };
 | |
|     pc2!.onIceCandidate = (candidate) {
 | |
|       if (candidate.candidate!.contains('relay')) {
 | |
|         unawaited(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);
 | |
| 
 | |
|     Future<void> dispose() async {
 | |
|       await Future.wait([
 | |
|         pc1!.close(),
 | |
|         pc2!.close(),
 | |
|       ]);
 | |
|       await Future.wait([
 | |
|         pc1!.dispose(),
 | |
|         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, s) {
 | |
|       Logs()
 | |
|           .e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s);
 | |
|     }
 | |
| 
 | |
|     // ignore: unawaited_futures
 | |
|     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>>> getIceServers() 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],
 | |
|       }
 | |
|     ];
 | |
|   }
 | |
| }
 |