public void StopConference(Action <string> callback) { // Detach signalling from the conference. Signalling.Detach((error) => { // Stop echo canceller. if (EnableSoftwareEchoCancellation) { OpusEchoCanceller.Stop(); OpusEchoCanceller = null; } Conference.OnLinkInit -= LogLinkInit; Conference.OnLinkUp -= LogLinkUp; Conference.OnLinkDown -= LogLinkDown; Conference = null; VideoStream.OnLinkInit -= AddRemoteVideoControl; VideoStream.OnLinkDown -= RemoveRemoteVideoControl; VideoStream = null; AudioStream = null; callback(error); }); }
private void StopConference() { // Detach signalling from the conference. Signalling.Detach((error) => { if (error != null) { Alert(error); } }); #if __ANDROID__ // Stop echo canceller. OpusEchoCanceller.Stop(); OpusEchoCanceller = null; #endif Conference.OnLinkInit -= LogLinkInit; Conference.OnLinkUp -= LogLinkUp; Conference.OnLinkDown -= LogLinkDown; Conference = null; VideoStream.OnLinkInit -= AddRemoteVideoControl; VideoStream.OnLinkDown -= RemoveRemoteVideoControl; VideoStream = null; AudioStream = null; }
// TODO: will move to dispose /// <summary> /// Stops the conference. /// </summary> /// <returns>The conference.</returns> private void StopConference() { try { #if __ANDROID__ // Stop echo canceller. OpusEchoCanceller.Stop(); OpusEchoCanceller = null; #endif conference.OnLinkInit -= LogLinkInit; conference.OnLinkUp -= LogLinkUp; conference.OnLinkDown -= LogLinkDown; conference.OnLinkOfferAnswer -= OnLinkSendOfferAnswer; conference.OnLinkCandidate -= OnLinkSendCandidate; conference = null; videoStream.OnLinkInit -= AddRemoteVideoControl; videoStream.OnLinkDown -= RemoveRemoteVideoControl; videoStream = null; audioStream = null; } catch (Exception ex) { FM.Log.Debug(ex.ToString()); } }
//Video Chat is the main form public void StartConference(Action <string> callback) { // Create a WebRTC audio stream description (requires a // reference to the local audio feed). AudioStream = new AudioStream(LocalMedia.LocalMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. VideoStream = new VideoStream(LocalMedia.LocalMediaStream); VideoStream.OnLinkInit += AddRemoteVideoControl; VideoStream.OnLinkDown += RemoveRemoteVideoControl; // Create a new IceLink conference. Conference = new FM.IceLink.Conference(IceLinkServerAddress, new Stream[] { AudioStream, VideoStream }); // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. Conference.RelayUsername = "******"; Conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. Conference.OnLinkInit += LogLinkInit; Conference.OnLinkUp += LogLinkUp; Conference.OnLinkDown += LogLinkDown; // Attach signalling to the conference. Signalling.Attach(Conference, SessionId, callback); }
/// <summary> /// Creates the local audio and video devices, from Icelink demo /// </summary> /// <param name="callType"></param> /// <param name="callback"></param> public void StartConference(string callType, Action <string> callback) { // Create a WebRTC audio stream description (requires a // reference to the local audio feed). AudioStream = new AudioStream(LocalMedia.LocalMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. VideoStream = new VideoStream(LocalMedia.LocalMediaStream); VideoStream.OnLinkInit += AddRemoteVideoControl; VideoStream.OnLinkDown += RemoveRemoteVideoControl; // Create a conference using our stream descriptions. Conference = new FM.IceLink.Conference(StunServerAddress, new Stream[] { AudioStream, VideoStream }); Conference.CandidateMode = CandidateMode.Early; // Use our pre-generated DTLS certificate. Conference.DtlsCertificate = Certificate; // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. Conference.RelayUsername = "******"; Conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. Conference.OnLinkInit += LogLinkInit; Conference.OnLinkUp += LogLinkUp; Conference.OnLinkDown += LogLinkDown; // Start echo canceller. if (EnableSoftwareEchoCancellation) { OpusEchoCanceller = new OpusEchoCanceller(OpusClockRate, OpusChannels, true); OpusEchoCanceller.Start(); } if (callType == "outgoing") { // Outgoing call Signalling.Call(Conference, Jid, callback); } else if (callType == "incoming") { // Incoming call Signalling.Answer(Conference, Jid, Sdp, callback); } }
private void StartConference() { ConferenceStarted = true; // Create a WebRTC audio stream description (requires a // reference to the local audio feed). AudioStream = new AudioStream(LocalMedia.LocalMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. VideoStream = new VideoStream(LocalMedia.LocalMediaStream); VideoStream.OnLinkInit += AddRemoteVideoControl; VideoStream.OnLinkDown += RemoveRemoteVideoControl; // Create a conference using our stream descriptions. Conference = new FM.IceLink.Conference(IceLinkServerAddress, new Stream[] { AudioStream, VideoStream }); // Use our pre-generated DTLS certificate. Conference.DtlsCertificate = Certificate; // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. Conference.RelayUsername = "******"; Conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. Conference.OnLinkInit += LogLinkInit; Conference.OnLinkUp += LogLinkUp; Conference.OnLinkDown += LogLinkDown; #if __ANDROID__ // Start echo canceller. OpusEchoCanceller = new OpusEchoCanceller(OpusClockRate, OpusChannels); OpusEchoCanceller.Start(); #endif Signalling.Attach(Conference, SessionId, (error) => { if (error != null) { Alert(error); } }); }
/// <summary> /// Begin outgoing call /// </summary> /// <param name="conference"></param> /// <param name="to"></param> /// <param name="callback"></param> public void Call(FM.IceLink.Conference conference, string to, Action <string> callback) { To = to; Messaging.Log.Info(">Call " + to); Conference = conference; // when IceLink generates an Offer or a Candidate, raise event Conference.OnLinkOfferAnswer += SendSessionInitiate; Conference.OnLinkCandidate += SendCandidate; Conference.OnUnhandledException += Conference_OnUnhandledException; // Icelink generate candidates Conference.Link(to); }
public void Detach(Action <string> callback) { if (UseWebSyncExtension) { // Leave the managed WebSync channel. WebSyncClient.LeaveConference(new LeaveConferenceArgs("/" + SessionId) { OnSuccess = (e) => { Conference = null; SessionId = null; callback(null); }, OnFailure = (e) => { callback(string.Format("Could not detach signalling from conference {0}. {1}", SessionId, e.Exception.Message)); } }); } else { // Unsubscribe from the WebSync channel. WebSyncClient.Unsubscribe(new UnsubscribeArgs("/" + SessionId) { OnSuccess = (e) => { // Detach our event handlers. Conference.OnLinkOfferAnswer -= SendOfferAnswer; Conference.OnLinkCandidate -= SendCandidate; WebSyncClient.OnNotify -= ReceiveOfferAnswerOrCandidate; Conference = null; SessionId = null; callback(null); }, OnFailure = (e) => { callback(string.Format("Could not detach signalling from conference {0}. {1}", SessionId, e.Exception.Message)); } }); } }
public void StopConference(Action <string> callback) { // Detach signalling from the conference. Signalling.Detach((error) => { Conference.OnLinkInit -= LogLinkInit; Conference.OnLinkUp -= LogLinkUp; Conference.OnLinkDown -= LogLinkDown; Conference = null; VideoStream.OnLinkInit -= AddRemoteVideoControl; VideoStream.OnLinkDown -= RemoveRemoteVideoControl; VideoStream = null; AudioStream = null; callback(error); }); }
/// <summary> /// Recipient accepted call /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void Xmpp_OnReceiveSessionAccept(object sender, Messaging.Jingle.JingleSdpEventArgs e) { Messaging.Log.Debug("OnReceiveSessionAccept > Conference.ReceiveOfferAnswer " + e.From); OfferAnswer oa = new OfferAnswer(); oa.SdpMessage = e.Sdp; oa.IsOffer = false; Messaging.Log.Info("Peer ID: " + e.From); Messaging.Log.Info("SA SDP in: " + e.Sdp); if (Conference == null) { Conference = ActivityService.GetInstance.App.GetConference(); } // send candidates to Icelink to negotiate connection Conference.ReceiveOfferAnswer(oa, e.From); }
/// <summary> /// hangup /// </summary> /// <param name="callback"></param> public void Detach(Action <string> callback) { Messaging.Log.Debug("Signalling.Detach"); //this.xmpp.OnReceiveSessionAccept -= Xmpp_OnReceiveSessionAccept; //this.xmpp.OnReceiveSessionInitiate -= Xmpp_OnReceiveSessionInitiate; //this.xmpp.OnReceiveCandidate -= Xmpp_OnReceiveCandidate; if (Conference != null) { Conference.OnLinkOfferAnswer -= SendSessionInitiate; Conference.OnLinkCandidate -= SendCandidate; Conference.OnUnhandledException -= Conference_OnUnhandledException; Conference = null; } if (callback != null) { Messaging.Log.Debug("Signallin.gDetach callback"); callback(null); } }
/// <summary> /// Answer incoming call /// </summary> /// <param name="conference"></param> /// <param name="to"></param> /// <param name="sdp"></param> /// <param name="callback"></param> public void Answer(FM.IceLink.Conference conference, string to, string sdp, Action <string> callback) { To = to; // Console.WriteLine ("Answer callback"); Conference = conference; Conference.OnLinkOfferAnswer += SendSessionAccept; Conference.OnLinkCandidate += SendCandidate; Conference.OnUnhandledException += Conference_OnUnhandledException; Conference.OnLinkRemoteOfferAnswer += LinkRemoteOfferAnswer; OfferAnswer oa = new OfferAnswer(); oa.SdpMessage = sdp; oa.IsOffer = true; Messaging.Log.Info("Peer ID: " + to); Messaging.Log.Info("SI SDP in:\n" + sdp); Messaging.Log.Debug("Answer > Conference.ReceiveOfferAnswer"); // send to Icelink to generate candidates Conference.ReceiveOfferAnswer(oa, to); }
public ConferenceWrapper(string sessionId, LocalMedia localMedia) { this.LocalMedia = localMedia; InitAudioAndVideoStreams(); // Create a conference using our stream descriptions. conference = new FM.IceLink.Conference(this.IceServers, new Stream[] { audioStream, videoStream }); // Use our pre-generated DTLS certificate. conference.DtlsCertificate = Certificate; // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. conference.RelayUsername = "******"; conference.RelayPassword = "******"; conference.ServerPort = 3478; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. conference.OnLinkInit += LogLinkInit; conference.OnLinkUp += LogLinkUp; conference.OnLinkDown += LogLinkDown; conference.OnLinkOfferAnswer += OnLinkSendOfferAnswer; conference.OnLinkCandidate += OnLinkSendCandidate; conference.Timeout = ConferenceTimeout; #if __ANDROID__ // Start echo canceller. OpusEchoCanceller = new OpusEchoCanceller(OpusClockRate, OpusChannels); OpusEchoCanceller.Start(); #endif this.sessionId = sessionId; }
//Video Chat is the main form public void StartConference(Action<string> callback) { // Create a WebRTC audio stream description (requires a // reference to the local audio feed). AudioStream = new AudioStream(LocalMedia.LocalMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. VideoStream = new VideoStream(LocalMedia.LocalMediaStream); VideoStream.OnLinkInit += AddRemoteVideoControl; VideoStream.OnLinkDown += RemoveRemoteVideoControl; // Create a new IceLink conference. Conference = new FM.IceLink.Conference(IceLinkServerAddress, new Stream[] { AudioStream, VideoStream }); // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. Conference.RelayUsername = "******"; Conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. Conference.OnLinkInit += LogLinkInit; Conference.OnLinkUp += LogLinkUp; Conference.OnLinkDown += LogLinkDown; // Attach signalling to the conference. Signalling.Attach(Conference, SessionId, callback); }
public void StopConference(Action<string> callback) { // Detach signalling from the conference. Signalling.Detach((error) => { Conference.OnLinkInit -= LogLinkInit; Conference.OnLinkUp -= LogLinkUp; Conference.OnLinkDown -= LogLinkDown; Conference = null; VideoStream.OnLinkInit -= AddRemoteVideoControl; VideoStream.OnLinkDown -= RemoveRemoteVideoControl; VideoStream = null; AudioStream = null; callback(error); }); }
public void Attach(FM.IceLink.Conference conference, Action<Exception> callback) { try { this.Conference = conference; conference.OnLinkOfferAnswer += (e) => { try { Signal signal = new Signal(); signal.PeerId = e.PeerId; signal.DataJson = e.OfferAnswer.ToJson(); #if USE_TCP_SIGNALLING TCPSender<Signal> sender = new TCPSender<Signal> (); if (sender.Connect (IPAddress, TCP_PORT_SIGNALLING)) { sender.Send (TCP_MESSAGE_OFFER_ANSWER, signal); sender.Disconnect (); } #else UDPSender<Signal> sender = new UDPSender<Signal>(); sender.Send(TCP_MESSAGE_OFFER_ANSWER,signal,this.IPAddress,TCP_PORT_SIGNALLING); #endif } catch (Exception ex) { Console.WriteLine(ex.Message); } }; conference.OnLinkCandidate += (e) => { try { Signal signal = new Signal(); signal.PeerId = e.PeerId; signal.DataJson = e.Candidate.ToJson(); #if USE_TCP_SIGNALLING TCPSender<Signal> sender = new TCPSender<Signal> (); if (sender.Connect (IPAddress, TCP_PORT_SIGNALLING)) { sender.Send (TCP_MESSAGE_CANDIDATE, signal); sender.Disconnect (); } #else UDPSender<Signal> sender = new UDPSender<Signal>(); sender.Send(TCP_MESSAGE_CANDIDATE,signal,this.IPAddress,TCP_PORT_SIGNALLING); #endif } catch (Exception ex) { Console.WriteLine(ex.Message); } }; } catch (Exception ex) { callback(ex); } }
public void StartConference(ViewGroup videoContainer, Action<Exception> callback) { try { var localVideoControl = LocalMedia.LocalVideoControl; var layoutManager = new AndroidLayoutManager (videoContainer); layoutManager.SetLocalVideoControl (localVideoControl); var videoStream = new VideoStream (LocalMedia.LocalStream); videoStream.OnLinkInit += (e) => { var remoteVideoControl = e.Link.GetRemoteVideoControl (); layoutManager.AddRemoteVideoControl (e.PeerId, remoteVideoControl); }; videoStream.OnLinkDown += (e) => { layoutManager.RemoveRemoteVideoControl (e.PeerId); }; // Create a conference using our stream descriptions. Conference = new FM.IceLink.Conference (videoStream); Conference.MaxLinks = 1; Signalling.Attach (Conference, (ex) => { if (ex != null) { ex = new Exception ("Could not attach signalling to conference.", ex); LastStartConferenceException = ex; } callback (ex); }); } catch (Exception ex) { LastStartConferenceException = ex; callback (ex); } }
public void StopLink() { try { if (Signalling != null) { Signalling.Stop (null); Signalling = null; } } catch {} try { if (LocalMedia != null) { LocalMedia.Stop (null); LocalMedia = null; } } catch {} try { if (Conference != null) { Conference.UnlinkAll (); Conference = null; } } catch {} }
//Video Chat is the main form public void StartConference(MainPage videoWindow, Action<Exception> callback) { if (!SignallingExists()) { callback(new Exception("Signalling must exist before starting a conference.")); } else if (!LocalMediaExists()) { callback(new Exception("Local media must exist before starting a conference.")); } else if (ConferenceExists()) { //trying to start a conference again callback(signalling.LastConferenceException); } else { try { var localMediaStream = localMedia.LocalStream; // This is our local video control, a WinForms Control or // WPF FrameworkElement. It is constantly updated with // our live video feed since we requested video above. // Add it directly to the UI or use the IceLink layout // manager, which we do below. var localVideoControl = localMedia.LocalVideoControl; // Create an IceLink layout manager, which makes the task // of arranging video controls easy. Give it a reference // to a WinForms control that can be filled with video feeds. // For WPF users, the WebRTC extension includes // WpfLayoutManager, which accepts a Canvas. var layoutManager = localMedia.LayoutManager; // Create a WebRTC audio stream description (requires a // reference to the local audio feed). var audioStream = new AudioStream(localMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. var videoStream = new VideoStream(localMediaStream); videoStream.OnLinkInit += (e) => { var remoteVideoControl = (FrameworkElement)e.Link.GetRemoteVideoControl(); layoutManager.AddRemoteVideoControl(e.PeerId, remoteVideoControl); // When double-clicked, mute/unmute the remote video. videoWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { // When double-tapped, mute/unmute the remote video. remoteVideoControl.DoubleTapped += (sender, ce) => { if (e.Link.RemoteVideoIsMuted()) { // Resume rendering incoming video. e.Link.UnmuteRemoteVideo(); } else { // Stop rendering incoming video. e.Link.MuteRemoteVideo(); } }; }); }; videoStream.OnLinkDown += (e) => { layoutManager.RemoveRemoteVideoControl(e.PeerId); }; // Create a new IceLink conference. conference = new FM.IceLink.Conference(IceLinkServerAddress, new Stream[] { audioStream, videoStream }); //Use our generated DTLS certificate. conference.DtlsCertificate = Certificate; // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. conference.RelayUsername = "******"; conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. conference.OnLinkInit += (e) => { Log.Info("Link to peer initializing..."); }; conference.OnLinkUp += (e) => { Log.Info("Link to peer is UP."); }; conference.OnLinkDown += (e) => { Log.InfoFormat("Link to peer is DOWN. {0}", e.Exception.Message); }; callback(null); } catch (Exception ex) { callback(ex); } } }
public void Detach(Action<string> callback) { if (UseWebSyncExtension) { // Leave the managed WebSync channel. WebSyncClient.LeaveConference(new LeaveConferenceArgs("/" + SessionId) { OnFailure = (e) => { callback(string.Format("Could not detach signalling from conference {0}. {1}", SessionId, e.Exception.Message)); }, OnSuccess = (e) => { Conference = null; SessionId = null; callback(null); } }); } else { // Unsubscribe from the WebSync channel. WebSyncClient.Unsubscribe(new UnsubscribeArgs("/" + SessionId) { OnFailure = (e) => { callback(string.Format("Could not detach signalling from conference {0}. {1}", SessionId, e.Exception.Message)); }, OnSuccess = (e) => { // Detach our event handlers. Conference.OnLinkOfferAnswer -= SendOfferAnswer; Conference.OnLinkCandidate -= SendCandidate; WebSyncClient.OnNotify -= ReceiveOfferAnswerOrCandidate; Conference = null; SessionId = null; callback(null); } }); } }
public void Attach(FM.IceLink.Conference conference, string sessionId, Action<string> callback) { Conference = conference; SessionId = sessionId; // IceLink includes a WebSync client extension that will // automatically manage session negotiation for you. If // you are not using WebSync, see the 'else' block for a // session negotiation template. if (UseWebSyncExtension) { // Manage the conference automatically using a WebSync // channel. P2P links will be created automatically to // peers that join the same channel. WebSyncClient.JoinConference(new JoinConferenceArgs("/" + SessionId, conference) { OnFailure = (e) => { callback(string.Format("Could not attach signalling to conference {0}. {1}", SessionId, e.Exception.Message)); }, OnSuccess = (e) => { callback(null); } }); } else { // When the conference generates an offer/answer or candidate, // we want to send it to the remote peer immediately. Conference.OnLinkOfferAnswer += SendOfferAnswer; Conference.OnLinkCandidate += SendCandidate; // When we receive an offer/answer or candidate, we want to // inform the conference immediately. WebSyncClient.OnNotify += ReceiveOfferAnswerOrCandidate; // Subscribe to a WebSync channel. When another client joins the same // channel, create a P2P link. When a client leaves, destroy it. WebSyncClient.Subscribe(new SubscribeArgs("/" + SessionId) { OnFailure = (e) => { callback(string.Format("Could not attach signalling to conference {0}. {1}", SessionId, e.Exception.Message)); }, OnReceive = (e) => { }, OnSuccess = (e) => { callback(null); } } .SetOnClientSubscribe((e) => { // Kick off a P2P link. var peerId = e.SubscribedClient.ClientId.ToString(); var peerState = e.SubscribedClient.BoundRecords; Conference.Link(peerId, peerState); }) .SetOnClientUnsubscribe((e) => { // Tear down a P2P link. var peerId = e.UnsubscribedClient.ClientId.ToString(); Conference.Unlink(peerId); })); } }
public void Attach(FM.IceLink.Conference conference, string sessionId, Action <string> callback) { Conference = conference; SessionId = sessionId; if (UseWebSyncExtension) { // Manage the conference automatically using a WebSync // channel. P2P links will be created automatically to // peers that join the same channel. WebSyncClient.JoinConference(new JoinConferenceArgs("/" + SessionId, conference) { OnFailure = (e) => { callback(string.Format("Could not join conference {0}. {1}", e.ConferenceChannel, e.Exception.Message)); }, OnSuccess = (e) => { callback(null); } }); } else { // When the conference generates an offer/answer or candidate, // we want to send it to the remote peer immediately. Conference.OnLinkOfferAnswer += SendOfferAnswer; Conference.OnLinkCandidate += SendCandidate; // When we receive an offer/answer or candidate, we want to // inform the conference immediately. WebSyncClient.OnNotify += ReceiveOfferAnswerOrCandidate; // Subscribe to a WebSync channel. When another client joins the same // channel, create a P2P link. When a client leaves, destroy it. WebSyncClient.Subscribe(new SubscribeArgs("/" + SessionId) { OnFailure = (e) => { callback(string.Format("Could not attach signalling to conference {0}. {1}", SessionId, e.Exception.Message)); }, OnReceive = (e) => { }, OnSuccess = (e) => { callback(null); } } .SetOnClientSubscribe((e) => { // Kick off a P2P link. var peerId = e.SubscribedClient.ClientId.ToString(); var peerState = e.SubscribedClient.BoundRecords; Conference.Link(peerId, peerState); }) .SetOnClientUnsubscribe((e) => { // Tear down a P2P link. var peerId = e.UnsubscribedClient.ClientId.ToString(); Conference.Unlink(peerId); })); } }
//Video Chat is the main form public void StartConference(MainPage videoWindow, Action <Exception> callback) { if (!SignallingExists()) { callback(new Exception("Signalling must exist before starting a conference.")); } else if (!LocalMediaExists()) { callback(new Exception("Local media must exist before starting a conference.")); } else if (ConferenceExists()) { //trying to start a conference again callback(signalling.LastConferenceException); } else { try { var localMediaStream = localMedia.LocalStream; // This is our local video control, a WinForms Control or // WPF FrameworkElement. It is constantly updated with // our live video feed since we requested video above. // Add it directly to the UI or use the IceLink layout // manager, which we do below. var localVideoControl = localMedia.LocalVideoControl; // Create an IceLink layout manager, which makes the task // of arranging video controls easy. Give it a reference // to a WinForms control that can be filled with video feeds. // For WPF users, the WebRTC extension includes // WpfLayoutManager, which accepts a Canvas. var layoutManager = localMedia.LayoutManager; // Create a WebRTC audio stream description (requires a // reference to the local audio feed). var audioStream = new AudioStream(localMediaStream); // Create a WebRTC video stream description (requires a // reference to the local video feed). Whenever a P2P link // initializes using this description, position and display // the remote video control on-screen by passing it to the // layout manager created above. Whenever a P2P link goes // down, remove it. var videoStream = new VideoStream(localMediaStream); videoStream.OnLinkInit += (e) => { var remoteVideoControl = (FrameworkElement)e.Link.GetRemoteVideoControl(); layoutManager.AddRemoteVideoControl(e.PeerId, remoteVideoControl); // When double-clicked, mute/unmute the remote video. videoWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async() => { // When double-tapped, mute/unmute the remote video. remoteVideoControl.DoubleTapped += (sender, ce) => { if (e.Link.RemoteVideoIsMuted()) { // Resume rendering incoming video. e.Link.UnmuteRemoteVideo(); } else { // Stop rendering incoming video. e.Link.MuteRemoteVideo(); } }; }); }; videoStream.OnLinkDown += (e) => { layoutManager.RemoveRemoteVideoControl(e.PeerId); }; // Create a new IceLink conference. conference = new FM.IceLink.Conference(IceLinkServerAddress, new Stream[] { audioStream, videoStream }); //Use our generated DTLS certificate. conference.DtlsCertificate = Certificate; // Supply TURN relay credentials in case we are behind a // highly restrictive firewall. These credentials will be // verified by the TURN server. conference.RelayUsername = "******"; conference.RelayPassword = "******"; // Add a few event handlers to the conference so we can see // when a new P2P link is created or changes state. conference.OnLinkInit += (e) => { Log.Info("Link to peer initializing..."); }; conference.OnLinkUp += (e) => { Log.Info("Link to peer is UP."); }; conference.OnLinkDown += (e) => { Log.InfoFormat("Link to peer is DOWN. {0}", e.Exception.Message); }; callback(null); } catch (Exception ex) { callback(ex); } } }