|
@@ -1,19 +1,26 @@
|
|
-
|
|
|
|
import 'dart:async';
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:convert';
|
|
|
|
+import 'dart:math';
|
|
|
|
|
|
import 'package:the_paint/src/generated/client.pbgrpc.dart';
|
|
import 'package:the_paint/src/generated/client.pbgrpc.dart';
|
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
|
|
|
|
|
|
|
+class WebRTCChannels extends Resolver {
|
|
|
|
+ WebRTCChannels._internal();
|
|
|
|
+
|
|
|
|
+ static final WebRTCChannels _singleton = WebRTCChannels._internal();
|
|
|
|
+ static final Map<String, Duplex<RTCDataChannelMessage>> mapf = {};
|
|
|
|
+
|
|
|
|
+ // RTCPeerConnection? _pc;
|
|
|
|
+ static bool connected = false;
|
|
|
|
|
|
-class WebRTCChannels {
|
|
|
|
- bool _inCalling = false;
|
|
|
|
- RTCPeerConnection? _pc;
|
|
|
|
- RTCDataChannel? _dc;
|
|
|
|
- PaintClient? _grpcClient;
|
|
|
|
|
|
+ Stream<RTCData>? _rtcData;
|
|
|
|
|
|
- WebRTCChannels();
|
|
|
|
|
|
+ factory WebRTCChannels() {
|
|
|
|
+ // _singleton.connect(() {});
|
|
|
|
+ return _singleton;
|
|
|
|
+ }
|
|
|
|
|
|
final Map<String, dynamic> _iceServers = {
|
|
final Map<String, dynamic> _iceServers = {
|
|
'iceServers': [
|
|
'iceServers': [
|
|
@@ -42,50 +49,58 @@ class WebRTCChannels {
|
|
Uri.parse('ws://localhost:8081/ws'),
|
|
Uri.parse('ws://localhost:8081/ws'),
|
|
);
|
|
);
|
|
|
|
|
|
- Future<void> connect(Function callback) async {
|
|
|
|
|
|
+ Future<void> connect() async {
|
|
|
|
+ if (connected) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ connected = true;
|
|
print("Connecting WEBSOCKET...");
|
|
print("Connecting WEBSOCKET...");
|
|
|
|
|
|
- _pc = await createPeerConnection(
|
|
|
|
|
|
+ _rtcData = _channel.stream
|
|
|
|
+ .map((event) => RTCData.fromBuffer(event))
|
|
|
|
+ .asBroadcastStream();
|
|
|
|
+
|
|
|
|
+ // await waitForData();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Future<RTCData> waitForData(String event /*add client id*/) async {
|
|
|
|
+ await connect();
|
|
|
|
+ return await _rtcData!.firstWhere((msg) => msg.event == event);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Future<Duplex<RTCDataChannelMessage>> createAndSubscribePeer(
|
|
|
|
+ RTCSessionDescription session) async {
|
|
|
|
+ Duplex<RTCDataChannelMessage> dup = Duplex();
|
|
|
|
+ var key = session.type!;
|
|
|
|
+ if (mapf.containsKey(key)) {
|
|
|
|
+ return mapf[key]!;
|
|
|
|
+ }
|
|
|
|
+ mapf[key] = dup;
|
|
|
|
+
|
|
|
|
+ var pc = await createPeerConnection(
|
|
_iceServers,
|
|
_iceServers,
|
|
);
|
|
);
|
|
|
|
|
|
- _pc!.onIceCandidate = (candidate) async {
|
|
|
|
- print('pc2: onIceCandidate: ${candidate.candidate}');
|
|
|
|
- await sendCandidate(candidate);
|
|
|
|
- };
|
|
|
|
|
|
+ await pc.setRemoteDescription(session);
|
|
|
|
+ var answer = await pc.createAnswer();
|
|
|
|
+ await pc.setLocalDescription(answer);
|
|
|
|
+ await sendLocalDescription(pc);
|
|
|
|
|
|
- _pc!.onDataChannel = (channel) {
|
|
|
|
- RTCDataChannel dc = channel;
|
|
|
|
- print('\nnew channel');
|
|
|
|
- dc.onDataChannelState = (state) {
|
|
|
|
|
|
+ pc.onDataChannel = (channel) {
|
|
|
|
+ print('\nnew channel ${channel.id} ${channel.label}');
|
|
|
|
+ channel.onDataChannelState = (state) {
|
|
print('\ndc2: change state: ${state.toString()}');
|
|
print('\ndc2: change state: ${state.toString()}');
|
|
};
|
|
};
|
|
- dc.onMessage = (data) {
|
|
|
|
- callback(MonitorReply.fromBuffer(data.binary));
|
|
|
|
- // dc.send(
|
|
|
|
- // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
|
|
|
|
|
|
+ channel.onMessage = (data) {
|
|
|
|
+ dup.streamToUI.add(data);
|
|
};
|
|
};
|
|
|
|
+ // add future to send message to channel!!!!
|
|
};
|
|
};
|
|
-
|
|
|
|
- final rtcData = _channel.stream.map((event) => RTCData.fromBuffer(event));
|
|
|
|
- await for (var msg in rtcData) {
|
|
|
|
- print(msg.event);
|
|
|
|
- switch (msg.event) {
|
|
|
|
- case "offer":
|
|
|
|
- await _pc!
|
|
|
|
- .setRemoteDescription(RTCSessionDescription(msg.data, msg.event));
|
|
|
|
- var answer = await _pc!.createAnswer();
|
|
|
|
- await _pc!.setLocalDescription(answer);
|
|
|
|
- await sendLocalDescription();
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- print("invalid event");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ return dup;
|
|
}
|
|
}
|
|
|
|
|
|
- sendLocalDescription() async {
|
|
|
|
- RTCSessionDescription? answer = await _pc!.getLocalDescription();
|
|
|
|
|
|
+ sendLocalDescription(RTCPeerConnection pc) async {
|
|
|
|
+ RTCSessionDescription? answer = await pc.getLocalDescription();
|
|
if (answer == null) {
|
|
if (answer == null) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
@@ -94,10 +109,31 @@ class WebRTCChannels {
|
|
_channel.sink.add(encodedData);
|
|
_channel.sink.add(encodedData);
|
|
}
|
|
}
|
|
|
|
|
|
- sendCandidate(RTCIceCandidate candidate) async {
|
|
|
|
|
|
+ @override
|
|
|
|
+ Future<Duplex<RTCDataChannelMessage>> dataChannel(Point<int> point) async {
|
|
final encodedData =
|
|
final encodedData =
|
|
- RTCData(data: jsonEncode(candidate.toMap()), event: "candidate")
|
|
|
|
- .writeToBuffer();
|
|
|
|
|
|
+ RTCData(data: point.toString(), event: "req_p").writeToBuffer();
|
|
_channel.sink.add(encodedData);
|
|
_channel.sink.add(encodedData);
|
|
|
|
+ var msg = await waitForData("offer");
|
|
|
|
+ var session = RTCSessionDescription(msg.data, msg.event);
|
|
|
|
+ return await createAndSubscribePeer(session);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class Duplex<T> {
|
|
|
|
+ StreamController<T> streamToUI = StreamController.broadcast();
|
|
|
|
+ StreamController<T> streamFromUI = StreamController.broadcast();
|
|
|
|
+
|
|
|
|
+ void send(T serialize) {
|
|
|
|
+ streamFromUI.add(serialize);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Stream<T> getStream() {
|
|
|
|
+ return streamToUI.stream;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+/* Return to who we need to connect*/
|
|
|
|
+abstract class Resolver {
|
|
|
|
+ Future<Duplex<RTCDataChannelMessage>> dataChannel(Point<int> point);
|
|
|
|
+}
|