Skip to content

Add web support. #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'dart:core';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.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:flutter/foundation.dart'
show debugDefaultTargetPlatformOverride;

Expand All @@ -10,13 +13,9 @@ 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(isDesktop())
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
if (WebRTC.platformIsDesktop)
debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
runApp(new MyApp());
}

Expand All @@ -33,7 +32,7 @@ enum DialogDemoAction {
class _MyAppState extends State<MyApp> {
List<RouteItem> items;
String _serverAddress = '';
SharedPreferences prefs;
KeyValueStore keyValueStore = KeyValueStore();
bool _datachannel = false;
@override
initState() {
Expand Down Expand Up @@ -71,9 +70,9 @@ class _MyAppState extends State<MyApp> {
}

_initData() async {
prefs = await SharedPreferences.getInstance();
await keyValueStore.init();
setState(() {
_serverAddress = prefs.getString('server') ?? 'demo.cloudwebrtc.com';
_serverAddress = keyValueStore.getString('server') ?? 'demo.cloudwebrtc.com';
});
}

Expand All @@ -85,12 +84,13 @@ class _MyAppState extends State<MyApp> {
// The value passed to Navigator.pop() or null.
if (value != null) {
if (value == DialogDemoAction.connect) {
prefs.setString('server', _serverAddress);
keyValueStore.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)));
}
}
});
Expand Down
49 changes: 22 additions & 27 deletions lib/src/call_sample/call_sample.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:core';
import 'signaling.dart';
import 'package:flutter_webrtc/webrtc.dart';
Expand All @@ -17,8 +16,6 @@ class CallSample extends StatefulWidget {

class _CallSampleState extends State<CallSample> {
Signaling _signaling;
String _displayName =
Platform.localHostname + '(' + Platform.operatingSystem + ")";
List<dynamic> _peers;
var _selfId;
RTCVideoRenderer _localRenderer = new RTCVideoRenderer();
Expand Down Expand Up @@ -50,8 +47,7 @@ class _CallSampleState extends State<CallSample> {

void _connect() async {
if (_signaling == null) {
_signaling = new Signaling(serverIP, _displayName)
..connect();
_signaling = new Signaling(serverIP)..connect();

_signaling.onStateChange = (SignalingState state) {
switch (state) {
Expand Down Expand Up @@ -114,9 +110,7 @@ class _CallSampleState extends State<CallSample> {
_signaling.switchCamera();
}

_muteMic() {

}
_muteMic() {}

_buildRow(context, peer) {
var self = (peer['id'] == _selfId);
Expand Down Expand Up @@ -164,25 +158,26 @@ class _CallSampleState extends State<CallSample> {
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: _inCalling
? new SizedBox(
width: 200.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
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: <Widget>[
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(
Expand Down
6 changes: 2 additions & 4 deletions lib/src/call_sample/data_channel_sample.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:core';
import 'dart:async';
import 'dart:typed_data';
import 'signaling.dart';
import 'package:flutter_webrtc/rtc_data_channel.dart';
import 'package:flutter_webrtc/webrtc.dart';

class DataChannelSample extends StatefulWidget {
static String tag = 'call_sample';
Expand All @@ -19,8 +19,6 @@ class DataChannelSample extends StatefulWidget {

class _DataChannelSampleState extends State<DataChannelSample> {
Signaling _signaling;
String _displayName =
Platform.localHostname + '(' + Platform.operatingSystem + ")";
List<dynamic> _peers;
var _selfId;
bool _inCalling = false;
Expand All @@ -47,7 +45,7 @@ class _DataChannelSampleState extends State<DataChannelSample> {

void _connect() async {
if (_signaling == null) {
_signaling = new Signaling(serverIP, _displayName)
_signaling = new Signaling(serverIP)
..connect();

_signaling.onDataChannelMessage = (dc, RTCDataChannelMessage data){
Expand Down
97 changes: 30 additions & 67 deletions lib/src/call_sample/signaling.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
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,
Expand All @@ -28,11 +31,10 @@ typedef void DataChannelCallback(RTCDataChannel dc);

class Signaling {
String _selfId = randomNumeric(6);
var _socket;
SimpleWebSocket _socket;
var _sessionId;
var _host;
var _port = 4443;
var _displayName;
var _peerConnections = new Map<String, RTCPeerConnection>();
var _dataChannels = new Map<String, RTCDataChannel>();
var _remoteCandidates = [];
Expand Down Expand Up @@ -84,7 +86,7 @@ class Signaling {
'optional': [],
};

Signaling(this._host, this._displayName);
Signaling(this._host);

close() {
if (_localStream != null) {
Expand Down Expand Up @@ -258,74 +260,36 @@ class Signaling {
}
}

Future<WebSocket> _connectForSelfSignedCert(String host, int port) async {
try {
Random r = new Random();
String key = base64.encode(List<int>.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 {
try {
/*
var url = 'ws://$_host:$_port';
_socket = await WebSocket.connect(url);
*/
_socket = await _connectForSelfSignedCert(_host, _port);
var url = 'wss://$_host:$_port';
_socket = SimpleWebSocket(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);
}
});
print('connect to $url');

_socket.onOpen = () {
print('onOpen');
this?.onStateChange(SignalingState.ConnectionOpen);
_send('new', {
'name': _displayName,
'name': DeviceInfo.label,
'id': _selfId,
'user_agent':
'flutter-webrtc/' + Platform.operatingSystem + '-plugin 0.0.1'
'user_agent': DeviceInfo.userAgent
});
} catch (e) {
};

_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]!');
if (this.onStateChange != null) {
this.onStateChange(SignalingState.ConnectionError);
this.onStateChange(SignalingState.ConnectionClosed);
}
}
};

await _socket.connect();
}

Future<MediaStream> createStream(media, user_screen) async {
Expand Down Expand Up @@ -440,7 +404,6 @@ class Signaling {
_send(event, data) {
data['type'] = event;
JsonEncoder encoder = new JsonEncoder();
if (_socket != null) _socket.add(encoder.convert(data));
print('send: ' + encoder.convert(data));
_socket.send(encoder.convert(data));
}
}
11 changes: 11 additions & 0 deletions lib/src/utils/device_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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';
}
}
11 changes: 11 additions & 0 deletions lib/src/utils/device_info_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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';
}
}
16 changes: 16 additions & 0 deletions lib/src/utils/key_value_store.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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<bool> setString(String key, String value) =>
_preferences.setString(key, value);
}
28 changes: 28 additions & 0 deletions lib/src/utils/key_value_store_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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<bool> setString(String key, String value) => _setValue(key, value);

Future<bool> _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);
}
}
Loading