public SymplePlayer(SymplePlayerOptions opts) { this.options = opts; this.options.format = "MJPEG"; this.options.engine = null; this.options.onCommand = (player, cmd) => { }; this.options.onStateChange = (player, state, message) => { }; if (this.options.engine == null) { var engine = SympleMedia.Instance.preferredCompatibleEngine(this.options.format); if (engine != null) { this.options.engine = engine.id; } } //this.bindEvents(); // here we would set up the event logic to bind UI buttons to actions like play/stop/mute/unmute/etc this.playing = false; }
public void initAndStartWebRTC() { #if NETFX_CORE JObject CLIENT_OPTIONS = new JObject(); CLIENT_OPTIONS["secure"] = true; CLIENT_OPTIONS["url"] = this.SignallingServerUrl; CLIENT_OPTIONS["peer"] = new JObject(); CLIENT_OPTIONS["peer"]["user"] = this.LocalPeerUsername; CLIENT_OPTIONS["peer"]["name"] = this.LocalPeerNameLabel; CLIENT_OPTIONS["peer"]["group"] = this.LocalPeerGroup; SymplePlayerOptions playerOptions = new SymplePlayerOptions(); playerOptions.userMediaConstraints.audioEnabled = this.AudioEnabled; playerOptions.userMediaConstraints.videoEnabled = this.VideoEnabled; playerOptions.CoreDispatcher = CoreDispatcher; playerOptions.engine = "WebRTC"; switch (UserType) { case StarUserType.TRAINEE: playerOptions.initiator = true; break; case StarUserType.MENTOR: playerOptions.initiator = false; break; default: break; } // WebRTC config // This is where you would add TURN servers for use in production RTCConfiguration WEBRTC_CONFIG = new RTCConfiguration { IceServers = new List <RTCIceServer> { new RTCIceServer { Url = "stun:stun.l.google.com:19302", Username = string.Empty, Credential = string.Empty }, new RTCIceServer { Url = "stun:stun1.l.google.com:19302", Username = string.Empty, Credential = string.Empty }, new RTCIceServer { Url = "stun:stun2.l.google.com:19302", Username = string.Empty, Credential = string.Empty }, new RTCIceServer { Url = "stun:stun3.l.google.com:19302", Username = string.Empty, Credential = string.Empty }, new RTCIceServer { Url = "stun:stun4.l.google.com:19302", Username = string.Empty, Credential = string.Empty }, new RTCIceServer { Url = "turn:numb.viagenie.ca", Username = "******", Credential = "0O@S&YfP$@56" } } }; playerOptions.rtcConfig = WEBRTC_CONFIG; //playerOptions.iceMediaConstraints = asdf; // TODO: not using iceMediaConstraints in latest code? playerOptions.onStateChange = (player, state, message) => { player.displayStatus(state); }; Messenger.Broadcast(SympleLog.LogTrace, "creating player"); player = new SymplePlayer(playerOptions); Messenger.Broadcast(SympleLog.LogTrace, "creating client"); client = new SympleClient(CLIENT_OPTIONS); client.on("announce", (peer) => { Messenger.Broadcast(SympleLog.LogInfo, "Authentication success: " + peer); }); client.on("addPeer", (peerObj) => { JObject peer = (JObject)peerObj; Messenger.Broadcast(SympleLog.LogInfo, "adding peer: " + peer); if (peer["user"] != null) { string peerUsername = (string)peer["user"]; Messenger.Broadcast(SympleLog.PeerAdded, peerUsername); } if (this.UserType == StarUserType.TRAINEE) { // the TRAINEE user waits for a peer with a specific username, then once it's connected it automatically starts sending video if ((string)peer["user"] == this.ExpectedRemoteVideoReceiverUsername && !videoPeerInitialized) { videoPeerInitialized = true; remoteVideoPeer = peer; startPlaybackAndRecording(); } } if (this.UserType == StarUserType.MENTOR) { // once the MENTOR user sees that the ANNOTATION_RECEIVER user has connected, the MENTOR user keeps track of that peer in order to send annotation messages to it. if ((string)peer["user"] == this.ExpectedRemoteAnnotationReceiverUsername && !annotationPeerInitialized) { annotationPeerInitialized = true; remoteAnnotationPeer = peer; Messenger.Broadcast(SympleLog.RemoteAnnotationReceiverConnected); // TODO: we could add code here to automatically send any annotation commands that were kept on a queue, so that if the HoloLens drops out and comes back, it can get all the annotations made by the mentor. } } }); client.on("presence", (presence) => { Messenger.Broadcast(SympleLog.LogDebug, "Recv presence: " + presence); }); client.on("removePeer", (peerObj) => { try { JObject peer = (JObject)peerObj; Messenger.Broadcast(SympleLog.LogInfo, "Removing peer: " + peer); if (peer != null) { if (peer["user"] != null) { string peerUsername = (string)peer["user"]; Messenger.Broadcast(SympleLog.PeerRemoved, peerUsername); } if (remoteVideoPeer != null && remoteVideoPeer["id"].Equals(peer["id"])) { Messenger.Broadcast(SympleLog.LogInfo, "Removing remote video peer"); videoPeerInitialized = false; remoteVideoPeer = null; if (player.engine != null) { player.engine.destroy(); player.engine = null; } } /* * if (remoteAnnotationPeer != null && remoteAnnotationPeer["id"].Equals(peer["id"])) * { * Messenger.Broadcast(SympleLog.LogInfo, "Removing remote annotation peer"); * annotationPeerInitialized = false; * remoteAnnotationPeer = null; * * Messenger.Broadcast(SympleLog.RemoteAnnotationReceiverDisconnected); * * // TODO: we could do some caching of annotation commands locally, in case the peer is reconnecting later in the future. * } */ } } catch (Exception e) { Messenger.Broadcast(SympleLog.LogInfo, "caught exception: " + e.Message); } }); client.on("message", (mObj) => { Messenger.Broadcast(SympleLog.LogTrace, "mObj.GetType().ToString(): " + mObj.GetType().ToString()); JObject m = (JObject)((Object[])mObj)[0]; var mFrom = m["from"]; JToken mFromId = null; if (mFrom.Type == JTokenType.Object) { mFromId = mFrom["id"]; } /* * if (remotePeer != null && !remotePeer["id"].Equals(mFromId)) * { * Messenger.Broadcast(SympleLog.LogDebug, "Dropping message from unknown peer: " + m); * return; * } */ if (m["offer"] != null) { switch (UserType) { case StarUserType.TRAINEE: Messenger.Broadcast(SympleLog.LogDebug, "Unexpected offer for one-way streaming"); break; case StarUserType.MENTOR: Messenger.Broadcast(SympleLog.LogDebug, "Receive offer: " + m["offer"]); remoteVideoPeer = (JObject)m["from"]; JObject playParams = new JObject(); // don't set requestedWebRtcCameraIndex here, because that's only for when sending video... instead we want to render whatever video we receive player.play(playParams); var engine = (SymplePlayerEngineWebRTC)player.engine; engine.recvRemoteSDP((JObject)m["offer"]); engine.sendLocalSDP = (desc) => { Messenger.Broadcast(SympleLog.LogDebug, "Send answer: " + desc); JObject sessionDesc = new JObject(); sessionDesc["sdp"] = desc.Sdp; if (desc.Type == Org.WebRtc.RTCSdpType.Answer) { sessionDesc["type"] = "answer"; } else if (desc.Type == Org.WebRtc.RTCSdpType.Offer) { sessionDesc["type"] = "offer"; } else if (desc.Type == Org.WebRtc.RTCSdpType.Pranswer) { sessionDesc["type"] = "pranswer"; } JObject parameters = new JObject(); parameters["to"] = remoteVideoPeer; parameters["type"] = "message"; parameters["answer"] = sessionDesc; client.send(parameters); }; engine.sendLocalCandidate = (cand) => { JObject candidateObj = new JObject(); candidateObj["candidate"] = cand.Candidate; candidateObj["sdpMid"] = cand.SdpMid; candidateObj["sdpMLineIndex"] = cand.SdpMLineIndex; JObject parameters = new JObject(); parameters["to"] = remoteVideoPeer; parameters["type"] = "message"; parameters["candidate"] = candidateObj; client.send(parameters); }; break; default: break; } } else if (m["answer"] != null) { switch (UserType) { case StarUserType.TRAINEE: SymplePlayerEngineWebRTC engine = (SymplePlayerEngineWebRTC)player.engine; string answerJsonString = JsonConvert.SerializeObject(m["answer"], Formatting.None); JObject answerParams = (JObject)m["answer"]; Messenger.Broadcast(SympleLog.LogTrace, "Receive answer: " + answerJsonString); engine.recvRemoteSDP(answerParams); break; case StarUserType.MENTOR: Messenger.Broadcast(SympleLog.LogDebug, "Unexpected answer for one-way streaming"); break; default: break; } } else if (m["candidate"] != null) { SymplePlayerEngineWebRTC engine = (SymplePlayerEngineWebRTC)player.engine; JObject candidateParams = (JObject)m["candidate"]; Messenger.Broadcast(SympleLog.LogDebug, "Using Candidate: " + candidateParams); engine.recvRemoteCandidate(candidateParams); } else { // the content of the message is unrecognized -- so it might be an annotation command. string jsonMessageString = m.ToString(Formatting.None); Messenger.Broadcast(SympleLog.IncomingMessage, jsonMessageString); } }); client.on("disconnect", (peer) => { Messenger.Broadcast(SympleLog.LogInfo, "Disconnected from server"); }); client.on("error", (error) => { Messenger.Broadcast(SympleLog.LogError, "Connection error: " + error); }); client.connect(); #else Messenger.Broadcast(SympleLog.LogInfo, "not actually connecting via webrtc because NETFX_CORE not defined (probably this is in the unity editor)"); #endif }