Skip to content

Commit 371e431

Browse files
committed
Modernize codebase: nullable refs, System.Text.Json, high-perf logging, polyfills
Enable nullable reference types across the solution and set C# language version to 14.0. Replace TinyJson with System.Text.Json source-generated serialization (SipSorceryJsonSerializerContext) and add a single-pass sanitizer in RTCSessionDescriptionInit.TryParse that escapes raw CR/LF inside JSON string values without allocating when no escaping is needed. Introduce compile-time logging extensions (LoggerMessage) for every subsystem (Media, DTLS-SRTP, ICE, RTCP, RTP, SCTP, SDP, STUN, WebRTC, sys/Net) to eliminate boxing and string formatting on hot paths. Add Meziantou.Polyfill and CommunityToolkit.HighPerformance packages to back-fill modern APIs (Span, FrozenDictionary, SearchValues, etc.) on older target frameworks. Add System.Text.Json, System.IO.Pipelines, and Microsoft.Bcl.Cryptography conditional references per TFM. Extract reusable low-level helpers into sys/: ValueStringBuilder, MemoryOperations, BinaryOperations, EncodingExtensions, HashExtensions, BouncyCastleExtensions, PolyfillExtensions, SocketConnection hierarchy, IPAddressExtension, and more. Move UdpReceiver from net/RTP to sys/Net. Convert file-scoped namespaces throughout. Update all projects and test infrastructure accordingly.
1 parent 4dc3ae5 commit 371e431

457 files changed

Lines changed: 53401 additions & 41124 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/OpenAIExamples/AliceAndBob/Program.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
using SIPSorcery.OpenAI.Realtime.Models;
3535
using SIPSorceryMedia.Abstractions;
3636
using SIPSorcery.Media;
37+
using System.Buffers;
38+
using CommunityToolkit.HighPerformance.Buffers;
3739

3840
namespace demo;
3941

@@ -161,11 +163,15 @@ private static void InitialiseWindowsAudioEndPoint(IWebRTCEndPoint webrtcEndPoin
161163

162164
webrtcEndPoint.OnAudioFrameReceived += (EncodedAudioFrame encodedAudioFrame) =>
163165
{
164-
var decodedSample = audioEncoder.DecodeAudio(encodedAudioFrame.EncodedAudio, AudioCommonlyUsedFormats.OpusWebRTC);
166+
using var buffer = new ArrayPoolBufferWriter<short>(8192);
167+
audioEncoder.DecodeAudio(encodedAudioFrame.EncodedAudio.Span, AudioCommonlyUsedFormats.OpusWebRTC, buffer);
168+
var decodedSample = buffer.WrittenSpan;
165169

166-
var samples = decodedSample
167-
.Select(s => new Complex(s / 32768f, 0f))
168-
.ToArray();
170+
var samples = new Complex[decodedSample.Length];
171+
for (int i = 0; i < samples.Length; i++)
172+
{
173+
samples[i] = new Complex(decodedSample[i] / 32768f, 0f);
174+
}
169175

170176
var frame = _audioScopeForm?.Invoke(() => _audioScopeForm.ProcessAudioSample(samples, audioScopeNumber));
171177
};

examples/SIPExamples/CustomAudioCodec/Program.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,23 @@
1414
//-----------------------------------------------------------------------------
1515

1616
using System;
17+
using System.Buffers;
18+
using System.Buffers.Binary;
1719
using System.Collections.Generic;
1820
using System.Linq;
21+
using System.Runtime.InteropServices;
1922
using System.Threading;
2023
using System.Threading.Tasks;
24+
using CommunityToolkit.HighPerformance;
25+
using CommunityToolkit.HighPerformance.Buffers;
2126
using Microsoft.Extensions.Logging;
2227
using Serilog;
2328
using Serilog.Extensions.Logging;
2429
using SIPSorcery.Media;
2530
using SIPSorcery.SIP;
2631
using SIPSorcery.SIP.App;
27-
using SIPSorceryMedia.Windows;
2832
using SIPSorceryMedia.Abstractions;
33+
using SIPSorceryMedia.Windows;
2934

3035
namespace demo
3136
{
@@ -50,15 +55,21 @@ public G729Codec()
5055
_g729Decoder = new G729Decoder();
5156
}
5257

53-
public short[] DecodeAudio(byte[] encodedSample, AudioFormat format)
58+
public void DecodeAudio(ReadOnlySpan<byte> encodedSample, AudioFormat format, IBufferWriter<short> destination)
5459
{
55-
var pcm = _g729Decoder.Process(encodedSample);
56-
return pcm.Where((x, i) => i % 2 == 0).Select((y, i) => (short)(pcm[i * 2 + 1] << 8 | pcm[i * 2])).ToArray();
60+
using var buffer = new ArrayPoolBufferWriter<byte>(8192);
61+
_g729Decoder.Process(encodedSample, buffer);
62+
63+
var pcm = buffer.WrittenSpan;
64+
for (int i = 0; i < pcm.Length / 2; i++)
65+
{
66+
destination.Write(BinaryPrimitives.ReadInt16LittleEndian(pcm.Slice(i * 2, 2)));
67+
}
5768
}
5869

59-
public byte[] EncodeAudio(short[] pcm, AudioFormat format)
70+
public void EncodeAudio(ReadOnlySpan<short> pcm, AudioFormat format, IBufferWriter<byte> destination)
6071
{
61-
return _g729Encoder.Process(pcm.SelectMany(x => new byte[] { (byte)(x), (byte)(x >> 8) }).ToArray());
72+
_g729Encoder.Process(MemoryMarshal.AsBytes(pcm), destination);
6273
}
6374
}
6475

@@ -105,7 +116,8 @@ static async Task Main()
105116
Console.WriteLine("Hanging up established call.");
106117
userAgent.Hangup();
107118
}
108-
};
119+
}
120+
;
109121

110122
exitCts.Cancel();
111123
};

examples/SIPExamples/RecordCall/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ private static void OnRtpPacketReceived(IPEndPoint remoteEndPoint, SDPMediaTypes
102102
{
103103
if (rtpPacket.Header.PayloadType == (int)SDPWellKnownMediaFormatsEnum.PCMA)
104104
{
105-
short pcm = NAudio.Codecs.ALawDecoder.ALawToLinearSample(sample[index]);
105+
short pcm = NAudio.Codecs.ALawDecoder.ALawToLinearSample(sample.Span[index]);
106106
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
107107
_waveFile.Write(pcmSample, 0, 2);
108108
}
109109
else
110110
{
111-
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
111+
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample.Span[index]);
112112
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
113113
_waveFile.Write(pcmSample, 0, 2);
114114
}

examples/SIPExamples/RecordIncomingCall/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ private static void OnRtpPacketReceived(IPEndPoint remoteEndPoint, SDPMediaTypes
8181
{
8282
if (rtpPacket.Header.PayloadType == (int)SDPWellKnownMediaFormatsEnum.PCMA)
8383
{
84-
short pcm = NAudio.Codecs.ALawDecoder.ALawToLinearSample(sample[index]);
84+
short pcm = NAudio.Codecs.ALawDecoder.ALawToLinearSample(sample.Span[index]);
8585
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
8686
_waveFile.Write(pcmSample, 0, 2);
8787
}
8888
else
8989
{
90-
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
90+
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample.Span[index]);
9191
byte[] pcmSample = new byte[] { (byte)(pcm & 0xFF), (byte)(pcm >> 8) };
9292
_waveFile.Write(pcmSample, 0, 2);
9393
}

examples/SIPExamples/SIPCallResampleAudio/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static void RtpSession_OnRtpPacketReceived(IPEndPoint remoteEndPoint, SD
9696

9797
for (int index = 0; index < sample.Length; index++)
9898
{
99-
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample[index]);
99+
short pcm = NAudio.Codecs.MuLawDecoder.MuLawToLinearSample(sample.Span[index]);
100100
float s16 = pcm / 32768f;
101101

102102
for (int i = 0; i < _ratio; i++)

examples/SIPExamples/VideoPhoneCmdLine/Program.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
//-----------------------------------------------------------------------------
3535

3636
using System;
37+
using System.Buffers;
3738
using System.Collections.Generic;
3839
using System.Drawing;
3940
using System.Linq;
@@ -110,13 +111,13 @@ public DecoderVideoSink(IVideoEncoder videoDecoder)
110111
public void GotVideoRtp(IPEndPoint remoteEndPoint, uint ssrc, uint seqnum, uint timestamp, int payloadID, bool marker, byte[] payload) =>
111112
throw new ApplicationException("This Video End Point requires full video frames rather than individual RTP packets.");
112113

113-
public void GotVideoFrame(IPEndPoint remoteEndPoint, uint timestamp, byte[] frame, VideoFormat format)
114+
public void GotVideoFrame(IPEndPoint remoteEndPoint, uint timestamp, ReadOnlyMemory<byte> frame, VideoFormat format)
114115
{
115116
if (OnVideoSinkDecodedSample != null)
116117
{
117118
try
118119
{
119-
foreach (var decoded in _videoDecoder.DecodeVideo(frame, VideoPixelFormatsEnum.Bgr, format.Codec))
120+
foreach (var decoded in _videoDecoder.DecodeVideo(frame.ToArray(), VideoPixelFormatsEnum.Bgr, format.Codec))
120121
{
121122
OnVideoSinkDecodedSample(decoded.Sample, decoded.Width, decoded.Height, (int)(decoded.Width * 3), VideoPixelFormatsEnum.Bgr);
122123
}

examples/TurnServerExample/Program.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//-----------------------------------------------------------------------------
1+
//-----------------------------------------------------------------------------
22
// Filename: Program.cs
33
//
44
// Description: An example TURN server (RFC 5766) console application that
@@ -104,13 +104,13 @@ static async Task RunClientDemo(int serverPort)
104104
var challenge = await ReceiveStun(stream);
105105
var errorAttr = challenge.Attributes.FirstOrDefault(
106106
a => a.AttributeType == STUNAttributeTypesEnum.ErrorCode);
107-
int errorCode = errorAttr.Value[2] * 100 + errorAttr.Value[3];
107+
int errorCode = errorAttr.Value.Span[2] * 100 + errorAttr.Value.Span[3];
108108
Console.WriteLine($"[Client] Got {errorCode} challenge with REALM and NONCE.");
109109

110110
// Extract nonce from the challenge
111111
var nonce = Encoding.UTF8.GetString(
112112
challenge.Attributes.First(
113-
a => a.AttributeType == STUNAttributeTypesEnum.Nonce).Value);
113+
a => a.AttributeType == STUNAttributeTypesEnum.Nonce).Value.Span);
114114

115115
Console.WriteLine("[Client] Sending authenticated Allocate...");
116116
var authReq = new STUNMessage(STUNMessageTypesEnum.Allocate);
@@ -191,12 +191,12 @@ await peer.SendAsync(response, response.Length,
191191
while (read < remaining)
192192
read += await stream.ReadAsync(full, 4 + read, remaining - read);
193193

194-
var dataInd = STUNMessage.ParseSTUNMessage(full, full.Length);
194+
var dataInd = STUNMessage.ParseSTUNMessage(full);
195195
var dataAttr = dataInd.Attributes.FirstOrDefault(
196196
a => a.AttributeType == STUNAttributeTypesEnum.Data);
197197
if (dataAttr != null)
198198
{
199-
Console.WriteLine($"[Client] Received via relay: \"{Encoding.UTF8.GetString(dataAttr.Value)}\"\n");
199+
Console.WriteLine($"[Client] Received via relay: \"{Encoding.UTF8.GetString(dataAttr.Value.Span)}\"\n");
200200
}
201201
}
202202
}
@@ -229,8 +229,9 @@ static byte[] ComputeHmacKey(string username, string realm, string password)
229229

230230
static async Task SendStun(NetworkStream stream, STUNMessage msg, byte[] hmacKey = null)
231231
{
232-
var bytes = msg.ToByteBuffer(hmacKey, false);
233-
await stream.WriteAsync(bytes, 0, bytes.Length);
232+
var bytes = new byte[msg.GetByteBufferSize(hmacKey, false)];
233+
msg.WriteToBuffer(bytes, hmacKey, false);
234+
await stream.WriteAsync(bytes);
234235
await stream.FlushAsync();
235236
}
236237

@@ -250,7 +251,7 @@ static async Task<STUNMessage> ReceiveStun(NetworkStream stream)
250251
while (read < remaining)
251252
read += await stream.ReadAsync(full, 4 + read, remaining - read);
252253

253-
return STUNMessage.ParseSTUNMessage(full, full.Length);
254+
return STUNMessage.ParseSTUNMessage(full);
254255
}
255256

256257
#endregion

examples/WebRTCExamples/FfmpegToWebRTC/FfmpegToWebRTC.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net8.0</TargetFramework>
66
<PlatformTarget>x64</PlatformTarget>
7+
<LangVersion>9.0</LangVersion>
78
</PropertyGroup>
89

910
<PropertyGroup>

examples/WebRTCExamples/FfmpegToWebRTC/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private static RTCPeerConnection Createpc(WebSocketContext context, SDPAudioVide
272272
if (media == SDPMediaTypesEnum.video && pc.VideoDestinationEndPoint != null)
273273
{
274274
//logger.LogDebug($"Forwarding {media} RTP packet to webrtc peer timestamp {rtpPkt.Header.Timestamp}.");
275-
pc.SendRtpRaw(media, rtpPkt.Payload, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType);
275+
pc.SendRtpRaw(media, rtpPkt.Payload.Span, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType);
276276
}
277277
};
278278
}

examples/WebRTCExamples/JanusWebRTCStream/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
//-----------------------------------------------------------------------------
1616

1717
using System;
18+
using System.Buffers;
1819
using System.Drawing;
1920
using System.Drawing.Imaging;
2021
using System.Linq;
22+
using System.Net;
2123
using System.Threading;
2224
using System.Threading.Tasks;
2325
using System.Windows.Forms;
@@ -86,7 +88,7 @@ static async Task Main()
8688

8789
MediaStreamTrack videoTrack = new MediaStreamTrack(videoSink.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv);
8890
pc.addTrack(videoTrack);
89-
pc.OnVideoFrameReceived += videoSink.GotVideoFrame;
91+
pc.OnVideoFrameReceived += (remoteEndPoint, timestamp, frame, format) => videoSink.GotVideoFrame(remoteEndPoint, timestamp, frame.ToArray(), format);
9092
videoSource.OnVideoSourceEncodedSample += pc.SendVideo;
9193

9294
pc.OnVideoFormatsNegotiated += (formats) =>

0 commit comments

Comments
 (0)