Created
June 28, 2024 22:37
-
-
Save gisborne/7a714c72444d91cbcd7f93a92aab73b3 to your computer and use it in GitHub Desktop.
Revisions
-
gisborne created this gist
Jun 28, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,140 @@ import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('MacOS STUN Test')), body: const STUNTest(), ), ); } } class STUNTest extends StatefulWidget { const STUNTest({Key? key}) : super(key: key); @override _STUNTestState createState() => _STUNTestState(); } class _STUNTestState extends State<STUNTest> { String _status = 'Initializing...'; RTCPeerConnection? _peerConnection; bool _gatheringComplete = false; @override void initState() { super.initState(); _createPeerConnection(); } Future<void> _createPeerConnection() async { _addLog('Creating peer connection...'); final Map<String, dynamic> configuration = { 'iceServers': [ { 'urls': [ 'stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302', ] } ], 'sdpSemantics': 'unified-plan', 'iceTransportPolicy': 'all', 'bundlePolicy': 'max-bundle', 'rtcpMuxPolicy': 'require', 'iceCandidatePoolSize': 0, 'enableDscp': true, 'enableIPv6': true, }; _peerConnection = await createPeerConnection(configuration); _peerConnection!.onIceCandidate = (RTCIceCandidate candidate) { if (candidate.candidate != null) { _processCandidate(candidate.candidate!); } }; _peerConnection!.onIceGatheringState = (RTCIceGatheringState state) { _addLog('ICE gathering state changed: $state'); if (state == RTCIceGatheringState.RTCIceGatheringStateComplete) { _gatheringComplete = true; _checkGatheringComplete(); } }; _peerConnection!.onConnectionState = (RTCPeerConnectionState state) { _addLog('Peer connection state changed: $state'); }; _addLog('Creating data channel...'); await _peerConnection!.createDataChannel('trigger', RTCDataChannelInit()); _addLog('Creating offer...'); RTCSessionDescription offer = await _peerConnection!.createOffer(); _addLog('Setting local description...'); await _peerConnection!.setLocalDescription(offer); // Set a longer timeout for M1 Macs Future.delayed(const Duration(seconds: 45), () { _checkGatheringComplete(); }); } void _processCandidate(String candidateString) { _addLog('Received candidate: $candidateString'); final parts = candidateString.split(' '); final type = parts.firstWhere((part) => part.startsWith('typ ')).split(' ')[1]; final ip = parts[4]; final port = parts[5]; _addLog('Candidate type: $type, IP: $ip, Port: $port'); } void _checkGatheringComplete() { if (!_gatheringComplete) { _addLog('Gathering timed out or completed'); } if (mounted) { setState(() { _status += '\nGathering process finished.'; }); } } void _addLog(String log) { print(log); if (mounted) { setState(() { _status += '\n$log'; }); } } @override Widget build(BuildContext context) { return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Text(_status), ), ); } @override void dispose() { _peerConnection?.close(); super.dispose(); } }