Skip to content

Commit 45caf6e

Browse files
authored
Added WebRTC demo that will use the VP8.Net encoder if/when it is ready. (#1359)
* Added WebRTC demo that will use the VP8.Net encoder if/when it is ready. * Remvoed some unused options.
1 parent aa5fb6a commit 45caf6e

File tree

7 files changed

+264
-8
lines changed

7 files changed

+264
-8
lines changed

examples/SIPExamples/UserAgentClient/Program.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@
2929
using SIPSorcery.Net;
3030
using SIPSorcery.SIP;
3131
using SIPSorcery.SIP.App;
32-
using SIPSorceryMedia.Abstractions;
3332
using SIPSorceryMedia.Windows;
3433

3534
namespace demo
3635
{
3736
class Program
3837
{
39-
//private static readonly string DEFAULT_DESTINATION_SIP_URI = "sips:[email protected]";
4038
private static readonly string DEFAULT_DESTINATION_SIP_URI = "sip:[email protected]";
4139

4240
private static Microsoft.Extensions.Logging.ILogger Log = NullLogger.Instance;

examples/WebRTCExamples/WebRTCClientVP8Net/Program.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@
2020
using System;
2121
using System.Drawing;
2222
using System.Drawing.Imaging;
23-
using System.IO;
2423
using System.Linq;
2524
using System.Net;
26-
using System.Text;
27-
using System.Threading;
2825
using System.Threading.Tasks;
2926
using System.Windows.Forms;
3027
using Microsoft.Extensions.Logging;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//-----------------------------------------------------------------------------
2+
// Filename: Program.cs
3+
//
4+
// Description: An example WebRTC server application that attempts to send a
5+
// test pattern to a browser peer and that uses a prototype .NET version of the
6+
// VP8 encoder. A web socket is used for signalling.
7+
//
8+
// The point of this demo is that it does not require any native libraries or
9+
// audio/video devices. This makes it a good palce to start for checking
10+
// whether a particular platform can be used to establish WebRTC connections
11+
// and get a media strem flowing.
12+
//
13+
// TODO: Not available until the VP8.NET project ports the encoder.
14+
//
15+
// Author(s):
16+
// Aaron Clauson ([email protected])
17+
//
18+
// History:
19+
// 12 Mar 2025 Aaron Clauson Created, Dublin, Ireland.
20+
//
21+
// License:
22+
// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
23+
//-----------------------------------------------------------------------------
24+
25+
using System;
26+
using System.Collections.Generic;
27+
using System.Linq;
28+
using System.Net;
29+
using System.Threading;
30+
using System.Threading.Tasks;
31+
using Microsoft.Extensions.Logging;
32+
using Microsoft.Extensions.Logging.Abstractions;
33+
using Serilog;
34+
using Serilog.Extensions.Logging;
35+
using SIPSorcery.Media;
36+
using SIPSorcery.Net;
37+
using Vpx.Net;
38+
using WebSocketSharp.Server;
39+
40+
namespace demo
41+
{
42+
class Program
43+
{
44+
private const int WEBSOCKET_PORT = 8081;
45+
private const string STUN_URL = "stun:stun.cloudflare.com";
46+
47+
private static Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;
48+
49+
static void Main()
50+
{
51+
Console.WriteLine("WebRTC Get Started");
52+
53+
logger = AddConsoleLogger();
54+
55+
// Start web socket.
56+
Console.WriteLine("Starting web socket server...");
57+
var webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT);
58+
webSocketServer.AddWebSocketService<WebRTCWebSocketPeer>("/", (peer) => peer.CreatePeerConnection = CreatePeerConnection);
59+
webSocketServer.Start();
60+
61+
Console.WriteLine($"Waiting for web socket connections on {webSocketServer.Address}:{webSocketServer.Port}...");
62+
Console.WriteLine("Press ctrl-c to exit.");
63+
64+
// Ctrl-c will gracefully exit the call at any point.
65+
ManualResetEvent exitMre = new ManualResetEvent(false);
66+
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
67+
{
68+
e.Cancel = true;
69+
exitMre.Set();
70+
};
71+
72+
// Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
73+
exitMre.WaitOne();
74+
}
75+
76+
private static Task<RTCPeerConnection> CreatePeerConnection()
77+
{
78+
RTCConfiguration config = new RTCConfiguration
79+
{
80+
iceServers = new List<RTCIceServer> { new RTCIceServer { urls = STUN_URL } },
81+
//X_BindAddress = IPAddress.Any
82+
};
83+
var pc = new RTCPeerConnection(config);
84+
85+
var testPatternSource = new VideoTestPatternSource();
86+
var videoEncoderEndPoint = new Vp8NetVideoEncoderEndPoint();
87+
var audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music });
88+
89+
MediaStreamTrack videoTrack = new MediaStreamTrack(videoEncoderEndPoint.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv);
90+
pc.addTrack(videoTrack);
91+
MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendRecv);
92+
pc.addTrack(audioTrack);
93+
94+
testPatternSource.OnVideoSourceRawSample += videoEncoderEndPoint.ExternalVideoSourceRawSample;
95+
videoEncoderEndPoint.OnVideoSourceEncodedSample += pc.SendVideo;
96+
audioSource.OnAudioSourceEncodedSample += pc.SendAudio;
97+
98+
pc.OnVideoFormatsNegotiated += (formats) => videoEncoderEndPoint.SetVideoSourceFormat(formats.First());
99+
pc.OnAudioFormatsNegotiated += (formats) => audioSource.SetAudioSourceFormat(formats.First());
100+
pc.onsignalingstatechange += () =>
101+
{
102+
logger.LogDebug($"Signalling state change to {pc.signalingState}.");
103+
104+
if (pc.signalingState == RTCSignalingState.have_local_offer)
105+
{
106+
logger.LogDebug($"Local SDP offer:\n{pc.localDescription.sdp}");
107+
}
108+
else if (pc.signalingState == RTCSignalingState.stable)
109+
{
110+
logger.LogDebug($"Remote SDP offer:\n{pc.remoteDescription.sdp}");
111+
}
112+
};
113+
114+
pc.onconnectionstatechange += async (state) =>
115+
{
116+
logger.LogDebug($"Peer connection state change to {state}.");
117+
118+
if (state == RTCPeerConnectionState.connected)
119+
{
120+
await audioSource.StartAudio();
121+
await testPatternSource.StartVideo();
122+
}
123+
else if (state == RTCPeerConnectionState.failed)
124+
{
125+
pc.Close("ice disconnection");
126+
}
127+
else if (state == RTCPeerConnectionState.closed)
128+
{
129+
await testPatternSource.CloseVideo();
130+
await audioSource.CloseAudio();
131+
}
132+
};
133+
134+
// Diagnostics.
135+
pc.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}");
136+
pc.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}");
137+
pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}.");
138+
pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}.");
139+
140+
// To test closing.
141+
//_ = Task.Run(async () =>
142+
//{
143+
// await Task.Delay(5000);
144+
145+
// audioSource.OnAudioSourceEncodedSample -= pc.SendAudio;
146+
// videoEncoderEndPoint.OnVideoSourceEncodedSample -= pc.SendVideo;
147+
148+
// logger.LogDebug("Closing peer connection.");
149+
// pc.Close("normal");
150+
//});
151+
152+
return Task.FromResult(pc);
153+
}
154+
155+
/// <summary>
156+
/// Adds a console logger. Can be omitted if internal SIPSorcery debug and warning messages are not required.
157+
/// </summary>
158+
private static Microsoft.Extensions.Logging.ILogger AddConsoleLogger()
159+
{
160+
var seriLogger = new LoggerConfiguration()
161+
.Enrich.FromLogContext()
162+
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Debug)
163+
.WriteTo.Console()
164+
.CreateLogger();
165+
var factory = new SerilogLoggerFactory(seriLogger);
166+
SIPSorcery.LogFactory.Set(factory);
167+
return factory.CreateLogger<Program>();
168+
}
169+
}
170+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
</PropertyGroup>
7+
8+
<PropertyGroup>
9+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
10+
<Prefer32Bit>false</Prefer32Bit>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
15+
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
16+
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
17+
<PackageReference Include="SIPSorcery.WebSocketSharp" Version="0.0.1" />
18+
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\..\..\src\SIPSorcery.csproj" />
23+
<ProjectReference Include="..\..\..\..\VP8.Net\src\VP8.Net.csproj" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!DOCTYPE html>
2+
<head>
3+
<script type="text/javascript">
4+
const WEBSOCKET_URL = "ws://127.0.0.1:8081/"
5+
6+
var pc, ws;
7+
8+
async function start() {
9+
pc = new RTCPeerConnection();
10+
11+
pc.ontrack = evt => {
12+
console.log("Adding track to video control.");
13+
document.querySelector('#videoCtl').srcObject = evt.streams[0];
14+
15+
evt.streams[0].onunmute = () => {
16+
console.log("Adding track to video control.");
17+
};
18+
19+
evt.streams[0].onended = () => {
20+
console.log("Track ended.");
21+
};
22+
}
23+
24+
pc.onicecandidate = evt => evt.candidate && ws.send(JSON.stringify(evt.candidate));
25+
26+
pc.onclose= () => {
27+
console.log("pc close");
28+
};
29+
30+
ws = new WebSocket(document.querySelector('#websockurl').value, []);
31+
ws.onmessage = async function (evt) {
32+
var obj = JSON.parse(evt.data);
33+
if (obj?.candidate) {
34+
pc.addIceCandidate(obj);
35+
}
36+
else if (obj?.sdp) {
37+
await pc.setRemoteDescription(new RTCSessionDescription(obj));
38+
pc.createAnswer()
39+
.then((answer) => pc.setLocalDescription(answer))
40+
.then(() => ws.send(JSON.stringify(pc.localDescription)));
41+
}
42+
};
43+
};
44+
45+
async function closePeer() {
46+
await pc?.close();
47+
await ws?.close();
48+
};
49+
50+
51+
52+
</script>
53+
</head>
54+
<body>
55+
56+
<video controls autoplay="autoplay" id="videoCtl" width="640" height="480"></video>
57+
58+
<div>
59+
<input type="text" id="websockurl" size="40" />
60+
<button type="button" class="btn btn-success" onclick="start();">Start</button>
61+
<button type="button" class="btn btn-success" onclick="closePeer();">Close</button>
62+
</div>
63+
64+
</body>
65+
66+
<script>
67+
document.querySelector('#websockurl').value = WEBSOCKET_URL;
68+
</script>

examples/WebRTCExamples/WebRTCSendAudio/Program.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
//-----------------------------------------------------------------------------
1616

1717
using System;
18-
using System.Collections.Generic;
1918
using System.Linq;
2019
using System.Net;
2120
using System.Threading;
@@ -33,7 +32,6 @@ namespace demo
3332
class Program
3433
{
3534
private const int WEBSOCKET_PORT = 8081;
36-
private const string STUN_URL = "stun:stun.sipsorcery.com";
3735

3836
private static Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;
3937

examples/WebRTCExamples/WebRTCSendAudio/WebRTCSendAudio.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
1515
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
1616
<PackageReference Include="SIPSorcery.WebSocketSharp" Version="0.0.1" />
17-
<PackageReference Include="SIPSorceryMedia.Windows" Version="8.0.7" />
1817
</ItemGroup>
1918

2019
<ItemGroup>

0 commit comments

Comments
 (0)