f7de6a912e0fefd6f5888f0acf144812301a411c
[platform/upstream/gstreamer.git] / webrtc / sendrecv / gst-sharp / WebRTCSendRecv.cs
1 using System;
2 using static System.Diagnostics.Debug;
3 using Gst;
4 using WebSocketSharp;
5 using Gst.WebRTC;
6 using Newtonsoft.Json;
7 using System.Net.Security;
8 using System.Security.Cryptography.X509Certificates;
9 using Gst.Sdp;
10 using System.Text;
11 using GLib;
12
13 namespace GstWebRTCDemo
14 {
15     class WebRtcClient : IDisposable
16     {
17         const string SERVER = "wss://127.0.0.1:8443";
18
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.";
24
25         readonly int _id;
26         readonly int _peerId;
27         readonly string _server;
28         readonly WebSocket _conn;
29         Pipeline pipe;
30         Element webrtc;
31         bool terminate;
32
33         public WebRtcClient(int id, int peerId, string server = SERVER)
34         {
35             _id = id;
36             _peerId = peerId;
37             _server = server;
38
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;
45
46             pipe = (Pipeline)Parse.Launch(PIPELINE_DESC);
47         }
48
49         bool validatCert(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
50         {
51             return true;
52         }
53
54         public void Connect()
55         {
56             _conn.ConnectAsync();
57         }
58
59         void SetupCall()
60         {
61             _conn.Send($"SESSION {_peerId}");
62         }
63
64         void OnClose(object sender, CloseEventArgs e)
65         {
66             Console.WriteLine("Closed: " + e.Reason);
67
68             terminate = true;
69         }
70
71         void OnError(object sender, ErrorEventArgs e)
72         {
73             Console.WriteLine("Error " + e.Message);
74
75             terminate = true;
76         }
77
78         void OnOpen(object sender, System.EventArgs e)
79         {
80             var ws = sender as WebSocket;
81             ws.SendAsync($"HELLO {_id}", (b) => Console.WriteLine($"Opened {b}"));
82         }
83
84         void OnMessage(object sender, MessageEventArgs args)
85         {
86             var msg = args.Data;
87             switch (msg)
88             {
89                 case "HELLO":
90                     SetupCall();
91                     break;
92                 case "SESSION_OK":
93                     StartPipeline();
94                     break;
95                 default:
96                     if (msg.StartsWith("ERROR")) {
97                         Console.WriteLine(msg);
98                         terminate = true;
99                     } else {
100                         HandleSdp(msg);
101                     }
102                     break;
103             }
104         }
105
106         void StartPipeline()
107         {
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");
115         }
116
117         #region Webrtc signal handlers
118         #region Incoming stream
119         void OnIncomingStream(object o, GLib.SignalArgs args)
120         {
121             var pad = args.Args[0] as Pad;
122             if (pad.Direction != PadDirection.Src)
123                 return;
124             var decodebin = ElementFactory.Make("decodebin");
125             decodebin.Connect("pad-added", OnIncomingDecodebinStream);
126             pipe.Add(decodebin);
127             decodebin.SyncStateWithParent();
128             webrtc.Link(decodebin);
129         }
130
131         void OnIncomingDecodebinStream(object o, SignalArgs args)
132         {
133             var pad = (Pad)args.Args[0];
134             if (!pad.HasCurrentCaps)
135             {
136                 Console.WriteLine($"{pad.Name} has no caps, ignoring");
137                 return;
138             }
139
140             var caps = pad.CurrentCaps;
141             Assert(!caps.IsEmpty);
142             Structure s = caps[0];
143             var name = s.Name;
144             if (name.StartsWith("video"))
145             {
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);
153             }
154             else if (name.StartsWith("audio"))
155             {
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);
164             }
165
166         }
167         #endregion
168
169         void OnIceCandidate(object o, GLib.SignalArgs args)
170         {
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);
175
176             _conn.SendAsync(iceMsg, (b) => { } );
177         }
178
179         void OnNegotiationNeeded(object o, GLib.SignalArgs args)
180         {
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);
186         }
187
188         void OnOfferCreated(Promise promise)
189         {
190             promise.Wait();
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);
196             promise.Interrupt();
197             SendSdpOffer(offer) ;
198         }
199         #endregion
200
201         void SendSdpOffer(WebRTCSessionDescription offer)
202         {
203             var text = offer.Sdp.AsText();
204             var obj = new { sdp = new { type = "offer", sdp = text } };
205             var json = JsonConvert.SerializeObject(obj);
206             Console.Write(json);
207
208             _conn.SendAsync(json, (b) => Console.WriteLine($"Send offer completed {b}"));
209         }
210
211         void HandleSdp(string message)
212         {
213             var msg = JsonConvert.DeserializeObject<dynamic>(message);
214
215             if (msg.sdp != null)
216             {
217                 var sdp = msg.sdp;
218                 if (sdp.type != null && sdp.type != "answer")
219                 {
220                     throw new Exception("Not an answer");
221                 }
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);
229             }
230             else if (msg.ice != null)
231             {
232                 var ice = msg.ice;
233                 string candidate = ice.candidate;
234                 uint sdpMLineIndex = ice.sdpMLineIndex;
235                 webrtc.Emit("add-ice-candidate", sdpMLineIndex, candidate);
236             }
237         }
238
239         public void Run()
240         {
241             // Wait until error, EOS or State Change
242             var bus = pipe.Bus;
243             do {
244                 var msg = bus.TimedPopFiltered (Gst.Constants.SECOND, MessageType.Error | MessageType.Eos | MessageType.StateChanged);
245                 // Parse message
246                 if (msg != null) {
247                     switch (msg.Type) {
248                     case MessageType.Error:
249                         string debug;
250                         GLib.GException exc;
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");
254                         terminate = true;
255                         break;
256                     case MessageType.Eos:
257                         Console.WriteLine ("End-Of-Stream reached.\n");
258                         terminate = true;
259                         break;
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));
267                         }
268                         break;
269                     default:
270                         // We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED
271                         Console.WriteLine ("Unexpected message received.");
272                         break;
273                     }
274                 }
275             } while (!terminate);
276         }
277         public void Dispose()
278         {
279             ((IDisposable)_conn).Dispose();
280             pipe.SetState(State.Null);
281             pipe.Dispose();
282         }
283     }
284
285     static class WebRtcSendRcv
286     {
287         const string SERVER = "wss://webrtc.nirbheek.in:8443";
288         static Random random = new Random();
289
290         public static void Main(string[] args)
291         {
292             // Initialize GStreamer
293             Gst.Application.Init (ref args);
294
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;
299
300             var ourId = random.Next(100, 10000);
301             Console.WriteLine($"PeerId:{peerId} OurId:{ourId} ");
302             var c = new WebRtcClient(ourId, peerId, server);
303             c.Connect();
304             c.Run();
305             c.Dispose();
306         }
307     }
308
309 }