From 4cf380fdf13c9080609976dce9a9cfdb3824cb1a Mon Sep 17 00:00:00 2001 From: Christian Hotz-Behofsits Date: Mon, 30 Dec 2013 14:26:02 +0100 Subject: [PATCH] refactored. untested --- lib/Messenger/messenger.dart | 5 +- .../src/peers/connections/connection.dart | 34 ++++ .../peers/connections/jswebrtcconnection.dart | 192 ++++++++++++++++++ lib/Messenger/src/peers/jswebrtcpeer.dart | 190 ++--------------- lib/Messenger/src/peers/peer.dart | 49 +---- lib/Messenger/src/peers/webrtcpeer.dart | 3 +- 6 files changed, 256 insertions(+), 217 deletions(-) create mode 100644 lib/Messenger/src/peers/connections/connection.dart create mode 100644 lib/Messenger/src/peers/connections/jswebrtcconnection.dart diff --git a/lib/Messenger/messenger.dart b/lib/Messenger/messenger.dart index a372b54..701069d 100644 --- a/lib/Messenger/messenger.dart +++ b/lib/Messenger/messenger.dart @@ -21,4 +21,7 @@ export 'src/signaling.dart'; part 'src/peers/peer.dart'; part 'src/peers/messagepassingpeer.dart'; part 'src/peers/webrtcpeer.dart'; -part 'src/peers/jswebrtcpeer.dart'; \ No newline at end of file +part 'src/peers/jswebrtcpeer.dart'; + +part 'src/peers/connections/connection.dart'; +part 'src/peers/connections/jswebrtcconnection.dart'; \ No newline at end of file diff --git a/lib/Messenger/src/peers/connections/connection.dart b/lib/Messenger/src/peers/connections/connection.dart new file mode 100644 index 0000000..78dfbfa --- /dev/null +++ b/lib/Messenger/src/peers/connections/connection.dart @@ -0,0 +1,34 @@ +part of messenger; + +abstract class Connection{ + T partner; + SignalingChannel sc; + ReadyState readyState; + StreamController readyStateEvent; + ///new message event stream + StreamController newMessageController; + Logger log; + + ///completer for connection + ///TODO: use another generic type + Completer connection_completer; + Completer listen_completer; + + /** + * setter: readyState + * + * @ TODO: make private + */ + changeReadyState(ReadyState readyState){ + //break if nothing will change + if (this.readyState == readyState) return; + + log.fine("change state: " + readyState.name); + + this.readyState = readyState; + readyStateEvent.add(readyState); + } + + send(String msg); + +} diff --git a/lib/Messenger/src/peers/connections/jswebrtcconnection.dart b/lib/Messenger/src/peers/connections/jswebrtcconnection.dart new file mode 100644 index 0000000..7fe449d --- /dev/null +++ b/lib/Messenger/src/peers/connections/jswebrtcconnection.dart @@ -0,0 +1,192 @@ +part of messenger; + +class JsWebRtcConnection extends Connection{ + T partner; + js.Proxy rtcPeerConnection; + js.Proxy dc; + + Map iceServers = {'iceServers':[{'url':'stun:stun.l.google.com:19302'}]}; + var pcConstraint = {}; + Map dataChannelOptions = {}; + + + + JsWebRtcConnection(log){ + dc=null; + + readyStateEvent = new StreamController.broadcast(); + newMessageController = new StreamController.broadcast(); + + /* create RTCPeerConnection */ + rtcPeerConnection = new js.Proxy(js.context.webkitRTCPeerConnection, + js.map(iceServers)); //TODO: add pcConstraints + + log.fine("created PeerConnection"); + + rtcPeerConnection.ondatachannel = (RtcDataChannelEvent event){ + log.info("datachannel received"); + + var proxy = new js.Proxy.fromBrowserObject(event); + + dc = proxy.channel; + + //dc = new js.Proxy(js.context.RTCDataChannel, js.context.JSON.stringify(event.channel)); + /* set channel events */ + dc.onmessage = (MessageEvent event)=>newMessageController.add(new NewMessageEvent(new Message(event.data))); + + dc.onopen = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); + dc.onclose = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); + dc.onerror = (x)=>log.shout("rtc error callback: " + x.toString()); + + + changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); + }; + } + + /** + * gotSignalingMessage callback + */ + gotSignalingMessage(NewMessageEvent data){ + switch(data.data.mtype){ + case MessageType.ICE_CANDIDATE: + //log.info("got ice candidate"); + + //deserialize + var iceCandidate = new js.Proxy(js.context.RTCIceCandidate, js.context.JSON.parse(data.data.msg)); + + //add candidate + rtcPeerConnection.addIceCandidate(iceCandidate); + break; + case MessageType.STRING: + //new Message. pass it! + break; + case MessageType.WEBRTC_OFFER: + log.fine("received sdp offer"); + + //deserialize + var sdp = new js.Proxy(js.context.RTCSessionDescription, js.context.JSON.parse(data.data.msg)); + + rtcPeerConnection.setRemoteDescription(sdp); + + createAnswer(); + break; + + case MessageType.WEBRTC_ANSWER: + log.fine("received sdp answer"); + + //deserialize + var sdp = new js.Proxy(js.context.RTCSessionDescription, js.context.JSON.parse(data.data.msg)); + + rtcPeerConnection.setRemoteDescription(sdp); + + log.fine("connection established"); + connection_completer.complete("wuhuu"); + listen_completer.complete("wuhuu"); + + //TODO: change status?! + break; + } + } + + /** + * RTC SDP answer + */ + createAnswer(){ + rtcPeerConnection.createAnswer((sdp_answer){ + log.fine("created sdp answer"); + + rtcPeerConnection.setLocalDescription(sdp_answer); + + //serialize sdp answer + final String jsonString = js.context.JSON.stringify(sdp_answer); + + //send ice candidate to other peer + sc.send(new Message(jsonString, MessageType.WEBRTC_ANSWER)); + log.fine("sdp answer sent"); + + connection_completer.complete("wuhuu"); + listen_completer.complete("wuhuu"); + }); + } + + /** + * listen for incoming connections + */ + Future listen(SignalingChannel sc){ + log.finest("start listening"); + + this.sc = sc; + + sc.onReceive.listen(gotSignalingMessage); + + /// add ice candidates + + rtcPeerConnection.onicecandidate = (event) { + log.finest("new ice candidate received"); + + if(event.candidate != null){ + try{ + var proxy = new js.Proxy.fromBrowserObject(event).candidate; + + //serialize ice candidate + final String jsonString = js.context.JSON.stringify(proxy); + + //send ice candidate to other peer + sc.send(new Message(jsonString, MessageType.ICE_CANDIDATE)); + + log.finest("new ice candidate serialized and sent to other peer"); + } catch(e){ + log.warning("bob error: could not add ice candidate " + e.toString()); + } + } + + }; + + return listen_completer.future; + } + + /** + * connect to WebrtcPeer + */ + Future connect(SignalingChannel sc){ + log.finest("try to connect"); + + //listen for incoming connection + listen(sc); + + /// create datachannel + + try { + dc = rtcPeerConnection.createDataChannel("sendDataChannel", js.map(dataChannelOptions)); + log.fine('created new data channel'); + + dc.onopen = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); + dc.onclose = (_)=>changeReadyState(dc.readyState); + + rtcPeerConnection.createOffer((sdp_offer){ + log.fine("create sdp offer"); + + rtcPeerConnection.setLocalDescription(sdp_offer); + + //serialize + final String jsonString = js.context.JSON.stringify(sdp_offer); + + sc.send(new Message(jsonString, MessageType.WEBRTC_OFFER)); + + }, (e){ + connection_completer.completeError(e, e.stackTrace); + }, {}); + + } catch (e) { + connection_completer.completeError("could not complete connect: ${e}", e.stackTrace); + } + + //return completer + return connection_completer.future; + } + + send(String msg){ + dc.send(msg.toString()); + } + +} \ No newline at end of file diff --git a/lib/Messenger/src/peers/jswebrtcpeer.dart b/lib/Messenger/src/peers/jswebrtcpeer.dart index 4ca3260..bf0dc9a 100644 --- a/lib/Messenger/src/peers/jswebrtcpeer.dart +++ b/lib/Messenger/src/peers/jswebrtcpeer.dart @@ -1,192 +1,26 @@ part of messenger; + + + + class JsWebRtcPeer extends Peer{ - js.Proxy rtcPeerConnection; - js.Proxy dc; - Map iceServers = {'iceServers':[{'url':'stun:stun.l.google.com:19302'}]}; - var pcConstraint = {}; - Map dataChannelOptions = {}; - - //TODO: what if not set yet. - SignalingChannel sc; - /** * constructor */ JsWebRtcPeer([String name="", Level logLevel=Level.FINE]):super(name, logLevel){ - dc = null; - - /* create RTCPeerConnection */ - rtcPeerConnection = new js.Proxy(js.context.webkitRTCPeerConnection, - js.map(iceServers)); //TODO: add pcConstraints - - log.fine("created PeerConnection"); - - rtcPeerConnection.ondatachannel = (RtcDataChannelEvent event){ - log.info("datachannel received"); - - var proxy = new js.Proxy.fromBrowserObject(event); - - dc = proxy.channel; - - //dc = new js.Proxy(js.context.RTCDataChannel, js.context.JSON.stringify(event.channel)); - /* set channel events */ - dc.onmessage = (MessageEvent event)=>newMessageController.add(new NewMessageEvent(new Message(event.data))); - - dc.onopen = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); - dc.onclose = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); - dc.onerror = (x)=>log.shout("rtc error callback: " + x.toString()); - - changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); - }; } - /** - * gotSignalingMessage callback - */ - gotSignalingMessage(NewMessageEvent data){ - switch(data.data.mtype){ - case MessageType.ICE_CANDIDATE: - //log.info("got ice candidate"); - - //deserialize - var iceCandidate = new js.Proxy(js.context.RTCIceCandidate, js.context.JSON.parse(data.data.msg)); - - //add candidate - rtcPeerConnection.addIceCandidate(iceCandidate); - break; - case MessageType.STRING: - //new Message. pass it! - break; - case MessageType.WEBRTC_OFFER: - log.fine("received sdp offer"); - - //deserialize - var sdp = new js.Proxy(js.context.RTCSessionDescription, js.context.JSON.parse(data.data.msg)); - - rtcPeerConnection.setRemoteDescription(sdp); - - createAnswer(); - break; - - case MessageType.WEBRTC_ANSWER: - log.fine("received sdp answer"); - - //deserialize - var sdp = new js.Proxy(js.context.RTCSessionDescription, js.context.JSON.parse(data.data.msg)); - - rtcPeerConnection.setRemoteDescription(sdp); - - log.fine("connection established"); - connection_completer.complete("wuhuu"); - listen_completer.complete("wuhuu"); - - //TODO: change status?! - break; - } - } - - /** - * RTC SDP answer - */ - createAnswer(){ - rtcPeerConnection.createAnswer((sdp_answer){ - log.fine("created sdp answer"); - - rtcPeerConnection.setLocalDescription(sdp_answer); - - //serialize sdp answer - final String jsonString = js.context.JSON.stringify(sdp_answer); - - //send ice candidate to other peer - sc.send(new Message(jsonString, MessageType.WEBRTC_ANSWER)); - log.fine("sdp answer sent"); - - connection_completer.complete("wuhuu"); - listen_completer.complete("wuhuu"); - }); - } - - - /** - * listen for incoming connections - */ - Future listen(SignalingChannel sc){ - log.finest("start listening"); - - this.sc = sc; - - sc.onReceive.listen(gotSignalingMessage); + listen(sc){ - /// add ice candidates - - rtcPeerConnection.onicecandidate = (event) { - log.finest("new ice candidate received"); - - if(event.candidate != null){ - try{ - var proxy = new js.Proxy.fromBrowserObject(event).candidate; - - //serialize ice candidate - final String jsonString = js.context.JSON.stringify(proxy); - - //send ice candidate to other peer - sc.send(new Message(jsonString, MessageType.ICE_CANDIDATE)); - - log.finest("new ice candidate serialized and sent to other peer"); - } catch(e){ - log.warning("bob error: could not add ice candidate " + e.toString()); - } - } - - }; - - return listen_completer.future; } - /** - * connect to WebrtcPeer - */ - Future connect(SignalingChannel sc){ - log.finest("try to connect"); + connect(sc){ - //listen for incoming connection - listen(sc); - - /// create datachannel - - try { - dc = rtcPeerConnection.createDataChannel("sendDataChannel", js.map(dataChannelOptions)); - log.fine('created new data channel'); - - dc.onopen = (_)=>changeReadyState(new ReadyState.fromDataChannel(dc.readyState)); - dc.onclose = (_)=>changeReadyState(dc.readyState); - - rtcPeerConnection.createOffer((sdp_offer){ - log.fine("create sdp offer"); - - rtcPeerConnection.setLocalDescription(sdp_offer); - - //serialize - final String jsonString = js.context.JSON.stringify(sdp_offer); - - sc.send(new Message(jsonString, MessageType.WEBRTC_OFFER)); - - }, (e){ - connection_completer.completeError(e, e.stackTrace); - }, {}); - - } catch (e) { - connection_completer.completeError("could not complete connect: ${e}", e.stackTrace); - } - - //return completer - return connection_completer.future; } - - + /** * disconnect Peer @@ -205,7 +39,9 @@ class JsWebRtcPeer extends Peer{ * * TODO: implementation */ - close(){} + close(){ + _connections.forEach((Peer p, Connection c)=>disconnect(p)); + } @@ -216,7 +52,11 @@ class JsWebRtcPeer extends Peer{ */ send(JsWebRtcPeer o, Message msg){ log.info("send message!"); - dc.send(msg.toString()); + + if(!_connections.containsKey(o)) + throw new StateError("list of connections does not contain peer ${o.name}"); + + _connections[o].send(msg.toString()); } } \ No newline at end of file diff --git a/lib/Messenger/src/peers/peer.dart b/lib/Messenger/src/peers/peer.dart index 03d825d..2f3845a 100644 --- a/lib/Messenger/src/peers/peer.dart +++ b/lib/Messenger/src/peers/peer.dart @@ -6,7 +6,7 @@ part of messenger; */ abstract class Peer{ - ///logging object + ///root logging object static final Logger parent_log = new Logger("Peer"); //list of all local peers @@ -17,36 +17,24 @@ abstract class Peer{ ///number of all local peer instances static int num = 0; - ///list of all connected peers - List _connections; - ///name of this peer instance String name; ///new message event stream StreamController newMessageController; - ///ready State of connection - ///todo: generalize and add to connections to support multiple states - ReadyState readyState; - - ///ready State event stream component - ///todo: generalize and add to connections to support multiple states - StreamController readyStateEvent; - - ///completer for connection - ///TODO: use another generic type - Completer connection_completer; - Completer listen_completer; + Map _connections; + /** * constuctor * - * todo: name has to be unique */ Peer([String name="", Level logLevel=Level.FINE]){ - this.name = (name.length < 1)?"peer" + (++num).toString():name; //set name of this peer instance + //set name of this peer instance + this.name = (name.length < 1)?"peer" + (++num).toString():name; + //is name is unique? if(peers.keys.contains(this.name)) throw new StateError("peer with name ${this.name} already exists!"); @@ -58,13 +46,7 @@ abstract class Peer{ print('${rec.loggerName} (${rec.level.name}): ${rec.message}'); }); - connection_completer = new Completer(); - listen_completer = new Completer(); - readyStateEvent = new StreamController.broadcast(); - newMessageController = new StreamController.broadcast(); - - _connections = new List(); - readyState=ReadyState.NEW; + newMessageController = new StreamController.broadcast(); log.info("new peer: #${num.toString()} ${this.name} "); @@ -72,20 +54,7 @@ abstract class Peer{ peers[this.name] = this; } - /** - * setter: readyState - * - * @ TODO: make private - */ - changeReadyState(ReadyState readyState){ - //break if nothing will change - if (this.readyState == readyState) return; - - log.fine("change state: " + readyState.name); - - this.readyState = readyState; - readyStateEvent.add(readyState); - } + /** * listen for incoming connections @@ -126,7 +95,7 @@ abstract class Peer{ /** * send message to all known peers */ - multicast(Message msg) => broadcast(_connections, msg); + multicast(Message msg) => broadcast(_connections.keys, msg); /** * getter: onstream event channel (stream) diff --git a/lib/Messenger/src/peers/webrtcpeer.dart b/lib/Messenger/src/peers/webrtcpeer.dart index 3172c61..de13f60 100644 --- a/lib/Messenger/src/peers/webrtcpeer.dart +++ b/lib/Messenger/src/peers/webrtcpeer.dart @@ -2,7 +2,7 @@ part of messenger; //TODO: support multiconnections //TODO: signalingchannel - +/* class WebRtcPeer extends Peer{ RtcPeerConnection rtcPeerConnection; var dataChannel; @@ -286,3 +286,4 @@ class WebRtcPeer extends Peer{ }); */ //} +*/ \ No newline at end of file