diff --git a/lib/main.dart b/lib/main.dart index 97ab45b..87da7ee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,7 @@ import 'dart:core'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_webrtc/webrtc.dart'; - -import 'src/utils/key_value_store.dart' - if (dart.library.js) 'src/utils/key_value_store_web.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride; @@ -13,9 +10,13 @@ import 'src/call_sample/call_sample.dart'; import 'src/call_sample/data_channel_sample.dart'; import 'src/route_item.dart'; +bool isDesktop() { + return Platform.isWindows || Platform.isLinux || Platform.isMacOS; +} + void main(){ - if (WebRTC.platformIsDesktop) - debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; + if(isDesktop()) + debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; runApp(new MyApp()); } @@ -32,7 +33,7 @@ enum DialogDemoAction { class _MyAppState extends State { List items; String _serverAddress = ''; - KeyValueStore keyValueStore = KeyValueStore(); + SharedPreferences prefs; bool _datachannel = false; @override initState() { @@ -70,9 +71,9 @@ class _MyAppState extends State { } _initData() async { - await keyValueStore.init(); + prefs = await SharedPreferences.getInstance(); setState(() { - _serverAddress = keyValueStore.getString('server') ?? 'demo.cloudwebrtc.com'; + _serverAddress = prefs.getString('server') ?? 'demo.cloudwebrtc.com'; }); } @@ -84,13 +85,12 @@ class _MyAppState extends State { // The value passed to Navigator.pop() or null. if (value != null) { if (value == DialogDemoAction.connect) { - keyValueStore.setString('server', _serverAddress); + prefs.setString('server', _serverAddress); Navigator.push( context, MaterialPageRoute( - builder: (BuildContext context) => _datachannel - ? DataChannelSample(ip: _serverAddress) - : CallSample(ip: _serverAddress))); + builder: (BuildContext context) => + _datachannel? DataChannelSample(ip: _serverAddress) : CallSample(ip: _serverAddress))); } } }); diff --git a/lib/src/call_sample/call_sample.dart b/lib/src/call_sample/call_sample.dart index 273807b..62f06f4 100644 --- a/lib/src/call_sample/call_sample.dart +++ b/lib/src/call_sample/call_sample.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'dart:io'; import 'dart:core'; import 'signaling.dart'; import 'package:flutter_webrtc/webrtc.dart'; @@ -16,6 +17,8 @@ class CallSample extends StatefulWidget { class _CallSampleState extends State { Signaling _signaling; + String _displayName = + Platform.localHostname + '(' + Platform.operatingSystem + ")"; List _peers; var _selfId; RTCVideoRenderer _localRenderer = new RTCVideoRenderer(); @@ -47,7 +50,8 @@ class _CallSampleState extends State { void _connect() async { if (_signaling == null) { - _signaling = new Signaling(serverIP)..connect(); + _signaling = new Signaling(serverIP, _displayName) + ..connect(); _signaling.onStateChange = (SignalingState state) { switch (state) { @@ -110,7 +114,9 @@ class _CallSampleState extends State { _signaling.switchCamera(); } - _muteMic() {} + _muteMic() { + + } _buildRow(context, peer) { var self = (peer['id'] == _selfId); @@ -158,26 +164,25 @@ class _CallSampleState extends State { floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButton: _inCalling ? new SizedBox( - width: 200.0, - child: new Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FloatingActionButton( - child: const Icon(Icons.switch_camera), - onPressed: _switchCamera, - ), - FloatingActionButton( - onPressed: _hangUp, - tooltip: 'Hangup', - child: new Icon(Icons.call_end), - backgroundColor: Colors.pink, - ), - FloatingActionButton( - child: const Icon(Icons.mic_off), - onPressed: _muteMic, - ) - ])) - : null, + width: 200.0, + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FloatingActionButton( + child: const Icon(Icons.switch_camera), + onPressed: _switchCamera, + ), + FloatingActionButton( + onPressed: _hangUp, + tooltip: 'Hangup', + child: new Icon(Icons.call_end), + backgroundColor: Colors.pink, + ), + FloatingActionButton( + child: const Icon(Icons.mic_off), + onPressed: _muteMic, + ) + ])) : null, body: _inCalling ? OrientationBuilder(builder: (context, orientation) { return new Container( diff --git a/lib/src/call_sample/data_channel_sample.dart b/lib/src/call_sample/data_channel_sample.dart index 0a354c2..60d1db0 100644 --- a/lib/src/call_sample/data_channel_sample.dart +++ b/lib/src/call_sample/data_channel_sample.dart @@ -4,7 +4,7 @@ import 'dart:core'; import 'dart:async'; import 'dart:typed_data'; import 'signaling.dart'; -import 'package:flutter_webrtc/webrtc.dart'; +import 'package:flutter_webrtc/rtc_data_channel.dart'; class DataChannelSample extends StatefulWidget { static String tag = 'call_sample'; @@ -19,6 +19,8 @@ class DataChannelSample extends StatefulWidget { class _DataChannelSampleState extends State { Signaling _signaling; + String _displayName = + Platform.localHostname + '(' + Platform.operatingSystem + ")"; List _peers; var _selfId; bool _inCalling = false; @@ -45,7 +47,7 @@ class _DataChannelSampleState extends State { void _connect() async { if (_signaling == null) { - _signaling = new Signaling(serverIP) + _signaling = new Signaling(serverIP, _displayName) ..connect(); _signaling.onDataChannelMessage = (dc, RTCDataChannelMessage data){ diff --git a/lib/src/call_sample/signaling.dart b/lib/src/call_sample/signaling.dart index 76c9e83..a993c83 100644 --- a/lib/src/call_sample/signaling.dart +++ b/lib/src/call_sample/signaling.dart @@ -1,13 +1,10 @@ import 'dart:convert'; import 'dart:async'; +import 'dart:io'; +import 'dart:math'; import 'package:flutter_webrtc/webrtc.dart'; import 'random_string.dart'; -import '../utils/device_info.dart' - if (dart.library.js) '../utils/device_info_web.dart'; -import '../utils/websocket.dart' - if (dart.library.js) '../utils/websocket_web.dart'; - enum SignalingState { CallStateNew, CallStateRinging, @@ -31,10 +28,11 @@ typedef void DataChannelCallback(RTCDataChannel dc); class Signaling { String _selfId = randomNumeric(6); - SimpleWebSocket _socket; + var _socket; var _sessionId; var _host; var _port = 4443; + var _displayName; var _peerConnections = new Map(); var _dataChannels = new Map(); var _remoteCandidates = []; @@ -86,7 +84,7 @@ class Signaling { 'optional': [], }; - Signaling(this._host); + Signaling(this._host, this._displayName); close() { if (_localStream != null) { @@ -260,36 +258,74 @@ class Signaling { } } + Future _connectForSelfSignedCert(String host, int port) async { + try { + Random r = new Random(); + String key = base64.encode(List.generate(8, (_) => r.nextInt(255))); + SecurityContext securityContext = new SecurityContext(); + HttpClient client = HttpClient(context: securityContext); + client.badCertificateCallback = + (X509Certificate cert, String host, int port) { + print('Allow self-signed certificate => $host:$port. '); + return true; + }; + + HttpClientRequest request = await client.getUrl( + Uri.parse('https://$host:$port/ws')); // form the correct url here + request.headers.add('Connection', 'Upgrade'); + request.headers.add('Upgrade', 'websocket'); + request.headers.add( + 'Sec-WebSocket-Version', '13'); // insert the correct version here + request.headers.add('Sec-WebSocket-Key', key.toLowerCase()); + + HttpClientResponse response = await request.close(); + Socket socket = await response.detachSocket(); + var webSocket = WebSocket.fromUpgradedSocket( + socket, + protocol: 'signaling', + serverSide: false, + ); + + return webSocket; + } catch (e) { + throw e; + } + } + void connect() async { - var url = 'wss://$_host:$_port'; - _socket = SimpleWebSocket(url); + try { + /* + var url = 'ws://$_host:$_port'; + _socket = await WebSocket.connect(url); + */ + _socket = await _connectForSelfSignedCert(_host, _port); - print('connect to $url'); + if (this.onStateChange != null) { + this.onStateChange(SignalingState.ConnectionOpen); + } + + _socket.listen((data) { + print('Recivied data: ' + data); + JsonDecoder decoder = new JsonDecoder(); + this.onMessage(decoder.convert(data)); + }, onDone: () { + print('Closed by server!'); + if (this.onStateChange != null) { + this.onStateChange(SignalingState.ConnectionClosed); + } + }); - _socket.onOpen = () { - print('onOpen'); - this?.onStateChange(SignalingState.ConnectionOpen); _send('new', { - 'name': DeviceInfo.label, + 'name': _displayName, 'id': _selfId, - 'user_agent': DeviceInfo.userAgent + 'user_agent': + 'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1' }); - }; - - _socket.onMessage = (message) { - print('Recivied data: ' + message); - JsonDecoder decoder = new JsonDecoder(); - this.onMessage(decoder.convert(message)); - }; - - _socket.onClose = (int code, String reason) { - print('Closed by server [$code => $reason]!'); + } catch (e) { if (this.onStateChange != null) { - this.onStateChange(SignalingState.ConnectionClosed); + this.onStateChange(SignalingState.ConnectionError); } - }; - - await _socket.connect(); + } } Future createStream(media, user_screen) async { @@ -404,6 +440,7 @@ class Signaling { _send(event, data) { data['type'] = event; JsonEncoder encoder = new JsonEncoder(); - _socket.send(encoder.convert(data)); + if (_socket != null) _socket.add(encoder.convert(data)); + print('send: ' + encoder.convert(data)); } } diff --git a/lib/src/utils/device_info.dart b/lib/src/utils/device_info.dart deleted file mode 100644 index 21617f4..0000000 --- a/lib/src/utils/device_info.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:io'; - -class DeviceInfo { - static String get label { - return Platform.localHostname + '(' + Platform.operatingSystem + ")"; - } - - static String get userAgent { - return 'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1'; - } -} diff --git a/lib/src/utils/device_info_web.dart b/lib/src/utils/device_info_web.dart deleted file mode 100644 index 0899668..0000000 --- a/lib/src/utils/device_info_web.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'dart:html' as HTML; - -class DeviceInfo { - static String get label { - return 'Flutter Web ( ' + HTML.window.navigator.userAgent + ' )'; - } - - static String get userAgent { - return 'flutter-webrtc/web-plugin 0.0.1'; - } -} diff --git a/lib/src/utils/key_value_store.dart b/lib/src/utils/key_value_store.dart deleted file mode 100644 index 56e5eb5..0000000 --- a/lib/src/utils/key_value_store.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'dart:async'; -import 'package:shared_preferences/shared_preferences.dart'; - -class KeyValueStore { - KeyValueStore(); - SharedPreferences _preferences; - - init() async { - _preferences = await SharedPreferences.getInstance(); - } - - String getString(String key) => _preferences.getString(key); - - Future setString(String key, String value) => - _preferences.setString(key, value); -} diff --git a/lib/src/utils/key_value_store_web.dart b/lib/src/utils/key_value_store_web.dart deleted file mode 100644 index 0fc445a..0000000 --- a/lib/src/utils/key_value_store_web.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:html'; - -class KeyValueStore { - KeyValueStore(); - Storage _storage; - - init() async { - _storage = window.localStorage; - } - - String getString(String key) => _storage[key]; - - Future setString(String key, String value) => _setValue(key, value); - - Future _setValue(String key, dynamic value) { - if (value is String) { - _storage[key] = value; - } else if (value is bool || value is double || value is int) { - _storage[key] = value.toString(); - } else if (value is List) { - _storage[key] = json.encode(value); - } - - return Future.value(true); - } -} diff --git a/lib/src/utils/websocket.dart b/lib/src/utils/websocket.dart deleted file mode 100644 index 6f031cc..0000000 --- a/lib/src/utils/websocket.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'dart:io'; -import 'dart:math'; -import 'dart:convert'; -import 'dart:async'; - -typedef void OnMessageCallback(dynamic msg); -typedef void OnCloseCallback(int code, String reason); -typedef void OnOpenCallback(); - -class SimpleWebSocket { - String _url; - var _socket; - OnOpenCallback onOpen; - OnMessageCallback onMessage; - OnCloseCallback onClose; - SimpleWebSocket(this._url); - - connect() async { - try { - _socket = await WebSocket.connect(_url); - //socket = await _connectForSelfSignedCert(_host, _port); - this?.onOpen(); - _socket.listen((data) { - this?.onMessage(data); - }, onDone: () { - this?.onClose(_socket.closeCode, _socket.closeReason); - }); - } catch (e) { - this.onClose(_socket.closeCode, _socket.closeReason); - } - } - - send(data) { - if (_socket != null) { - _socket.add(data); - print('send: $data'); - } - } - - close() { - _socket.close(); - } - - Future _connectForSelfSignedCert(String host, int port) async { - try { - Random r = new Random(); - String key = base64.encode(List.generate(8, (_) => r.nextInt(255))); - SecurityContext securityContext = new SecurityContext(); - HttpClient client = HttpClient(context: securityContext); - client.badCertificateCallback = - (X509Certificate cert, String host, int port) { - print('Allow self-signed certificate => $host:$port. '); - return true; - }; - - HttpClientRequest request = await client.getUrl( - Uri.parse('https://$host:$port/ws')); // form the correct url here - request.headers.add('Connection', 'Upgrade'); - request.headers.add('Upgrade', 'websocket'); - request.headers.add( - 'Sec-WebSocket-Version', '13'); // insert the correct version here - request.headers.add('Sec-WebSocket-Key', key.toLowerCase()); - - HttpClientResponse response = await request.close(); - Socket socket = await response.detachSocket(); - var webSocket = WebSocket.fromUpgradedSocket( - socket, - protocol: 'signaling', - serverSide: false, - ); - - return webSocket; - } catch (e) { - throw e; - } - } -} diff --git a/lib/src/utils/websocket_web.dart b/lib/src/utils/websocket_web.dart deleted file mode 100644 index f7ecdc5..0000000 --- a/lib/src/utils/websocket_web.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'dart:html'; - -typedef void OnMessageCallback(dynamic msg); -typedef void OnCloseCallback(int code, String reason); -typedef void OnOpenCallback(); - -class SimpleWebSocket { - String _url; - var _socket; - OnOpenCallback onOpen; - OnMessageCallback onMessage; - OnCloseCallback onClose; - - SimpleWebSocket(this._url); - - connect() async { - try { - _socket = WebSocket(_url); - _socket.onOpen.listen((e) { - this?.onOpen(); - }); - - _socket.onMessage.listen((e) { - this?.onMessage(e.data); - }); - - _socket.onClose.listen((e) { - this?.onClose(e.code, e.reason); - }); - } catch (e) { - this?.onClose(e.code, e.reason); - } - } - - send(data) { - if (_socket != null && _socket.readyState == WebSocket.OPEN) { - _socket.send(data); - print('send: $data'); - } else { - print('WebSocket not connected, message $data not sent'); - } - } - - close() { - _socket.close(); - } -} diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 3940fd7..0000000 --- a/web/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Flutter WebRTC Demo - - - - -