2 using static System.Diagnostics.Debug;
7 using System.Net.Security;
8 using System.Security.Cryptography.X509Certificates;
13 namespace GstWebRTCDemo
15 class WebRtcClient : IDisposable
17 const string SERVER = "wss://127.0.0.1:8443";
19 const string PIPELINE_DESC = @"webrtcbin name=sendrecv bundle-policy=max-bundle
20 videotestsrc is-live=true pattern=ball ! videoconvert ! queue ! vp8enc deadline=1 ! rtpvp8pay !
21 queue ! application/x-rtp,media=video,encoding-name=VP8,payload=97 ! sendrecv.
22 audiotestsrc is-live=true wave=red-noise ! audioconvert ! audioresample ! queue ! opusenc ! rtpopuspay !
23 queue ! application/x-rtp,media=audio,encoding-name=OPUS,payload=96 ! sendrecv.";
27 readonly string _server;
28 readonly WebSocket _conn;
33 public WebRtcClient(int id, int peerId, string server = SERVER)
39 _conn = new WebSocket(_server);
40 _conn.SslConfiguration.ServerCertificateValidationCallback = validatCert;
41 _conn.OnOpen += OnOpen;
42 _conn.OnError += OnError;
43 _conn.OnMessage += OnMessage;
44 _conn.OnClose += OnClose;
46 pipe = (Pipeline)Parse.Launch(PIPELINE_DESC);
49 bool validatCert(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
61 _conn.Send($"SESSION {_peerId}");
64 void OnClose(object sender, CloseEventArgs e)
66 Console.WriteLine("Closed: " + e.Reason);
71 void OnError(object sender, ErrorEventArgs e)
73 Console.WriteLine("Error " + e.Message);
78 void OnOpen(object sender, System.EventArgs e)
80 var ws = sender as WebSocket;
81 ws.SendAsync($"HELLO {_id}", (b) => Console.WriteLine($"Opened {b}"));
84 void OnMessage(object sender, MessageEventArgs args)
96 if (msg.StartsWith("ERROR")) {
97 Console.WriteLine(msg);
108 webrtc = pipe.GetByName("sendrecv");
109 Assert(webrtc != null);
110 webrtc.Connect("on-negotiation-needed", OnNegotiationNeeded);
111 webrtc.Connect("on-ice-candidate", OnIceCandidate);
112 webrtc.Connect("pad-added", OnIncomingStream);
113 pipe.SetState(State.Playing);
114 Console.WriteLine("Playing");
117 #region Webrtc signal handlers
118 #region Incoming stream
119 void OnIncomingStream(object o, GLib.SignalArgs args)
121 var pad = args.Args[0] as Pad;
122 if (pad.Direction != PadDirection.Src)
124 var decodebin = ElementFactory.Make("decodebin");
125 decodebin.Connect("pad-added", OnIncomingDecodebinStream);
127 decodebin.SyncStateWithParent();
128 webrtc.Link(decodebin);
131 void OnIncomingDecodebinStream(object o, SignalArgs args)
133 var pad = (Pad)args.Args[0];
134 if (!pad.HasCurrentCaps)
136 Console.WriteLine($"{pad.Name} has no caps, ignoring");
140 var caps = pad.CurrentCaps;
141 Assert(!caps.IsEmpty);
142 Structure s = caps[0];
144 if (name.StartsWith("video"))
146 var q = ElementFactory.Make("queue");
147 var conv = ElementFactory.Make("videoconvert");
148 var sink = ElementFactory.Make("autovideosink");
149 pipe.Add(q, conv, sink);
150 pipe.SyncChildrenStates();
151 pad.Link(q.GetStaticPad("sink"));
152 Element.Link(q, conv, sink);
154 else if (name.StartsWith("audio"))
156 var q = ElementFactory.Make("queue");
157 var conv = ElementFactory.Make("audioconvert");
158 var resample = ElementFactory.Make("audioresample");
159 var sink = ElementFactory.Make("autoaudiosink");
160 pipe.Add(q, conv, resample, sink);
161 pipe.SyncChildrenStates();
162 pad.Link(q.GetStaticPad("sink"));
163 Element.Link(q, conv, resample, sink);
169 void OnIceCandidate(object o, GLib.SignalArgs args)
171 var index = (uint)args.Args[0];
172 var cand = (string)args.Args[1];
173 var obj = new { ice = new { sdpMLineIndex = index, candidate = cand } };
174 var iceMsg = JsonConvert.SerializeObject(obj);
176 _conn.SendAsync(iceMsg, (b) => { } );
179 void OnNegotiationNeeded(object o, GLib.SignalArgs args)
181 var webRtc = o as Element;
182 Assert(webRtc != null, "not a webrtc object");
183 Promise promise = new Promise(OnOfferCreated, webrtc.Handle, null); // webRtc.Handle, null);
184 Structure structure = new Structure("struct");
185 webrtc.Emit("create-offer", structure, promise);
188 void OnOfferCreated(Promise promise)
191 var reply = promise.RetrieveReply();
192 var gval = reply.GetValue("offer");
193 WebRTCSessionDescription offer = (WebRTCSessionDescription)gval.Val;
194 promise = new Promise();
195 webrtc.Emit("set-local-description", offer, promise);
197 SendSdpOffer(offer) ;
201 void SendSdpOffer(WebRTCSessionDescription offer)
203 var text = offer.Sdp.AsText();
204 var obj = new { sdp = new { type = "offer", sdp = text } };
205 var json = JsonConvert.SerializeObject(obj);
208 _conn.SendAsync(json, (b) => Console.WriteLine($"Send offer completed {b}"));
211 void HandleSdp(string message)
213 var msg = JsonConvert.DeserializeObject<dynamic>(message);
218 if (sdp.type != null && sdp.type != "answer")
220 throw new Exception("Not an answer");
222 string sdpAns = sdp.sdp;
223 Console.WriteLine($"received answer:\n{sdpAns}");
224 SDPMessage.New(out SDPMessage sdpMsg);
225 SDPMessage.ParseBuffer(ASCIIEncoding.Default.GetBytes(sdpAns), (uint)sdpAns.Length, sdpMsg);
226 var answer = WebRTCSessionDescription.New(WebRTCSDPType.Answer, sdpMsg);
227 var promise = new Promise();
228 webrtc.Emit("set-remote-description", answer, promise);
230 else if (msg.ice != null)
233 string candidate = ice.candidate;
234 uint sdpMLineIndex = ice.sdpMLineIndex;
235 webrtc.Emit("add-ice-candidate", sdpMLineIndex, candidate);
241 // Wait until error, EOS or State Change
244 var msg = bus.TimedPopFiltered (Gst.Constants.SECOND, MessageType.Error | MessageType.Eos | MessageType.StateChanged);
248 case MessageType.Error:
251 msg.ParseError (out exc, out debug);
252 Console.WriteLine ("Error received from element {0}: {1}", msg.Src.Name, exc.Message);
253 Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
256 case MessageType.Eos:
257 Console.WriteLine ("End-Of-Stream reached.\n");
260 case MessageType.StateChanged:
261 // We are only interested in state-changed messages from the pipeline
262 if (msg.Src == pipe) {
263 State oldState, newState, pendingState;
264 msg.ParseStateChanged (out oldState, out newState, out pendingState);
265 Console.WriteLine ("Pipeline state changed from {0} to {1}:",
266 Element.StateGetName (oldState), Element.StateGetName (newState));
270 // We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED
271 Console.WriteLine ("Unexpected message received.");
275 } while (!terminate);
277 public void Dispose()
279 ((IDisposable)_conn).Dispose();
280 pipe.SetState(State.Null);
285 static class WebRtcSendRcv
287 const string SERVER = "wss://webrtc.nirbheek.in:8443";
288 static Random random = new Random();
290 public static void Main(string[] args)
292 // Initialize GStreamer
293 Gst.Application.Init (ref args);
295 if (args.Length == 0)
296 throw new Exception("need peerId");
297 int peerId = Int32.Parse(args[0]);
298 var server = (args.Length > 1) ? args[1] : SERVER;
300 var ourId = random.Next(100, 10000);
301 Console.WriteLine($"PeerId:{peerId} OurId:{ourId} ");
302 var c = new WebRtcClient(ourId, peerId, server);