Skip to content

Commit 66a312e

Browse files
author
Paulo Morgado
committed
Refactor codebase to utilize Span<byte> and ReadOnlySpan<byte> for improved memory efficiency. Introduced utility methods in BinaryOperations and MemoryOperations for big-endian and little-endian data handling. Updated NetConvert to use BinaryPrimitives for network byte order conversions. Marked several methods as obsolete to guide towards new span-based APIs. Refactored AudioEncoder and SCTP-related classes for enhanced performance and reduced memory overhead. Overall, these changes optimize memory usage and improve performance across the codebase.
1 parent 484fca8 commit 66a312e

33 files changed

+516
-363
lines changed

src/app/Media/Codecs/AudioEncoder.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Collections.Generic;
1919
using System.Linq;
2020
using SIPSorceryMedia.Abstractions;
21+
using SIPSorcery.Sys;
2122
using Concentus.Enums;
2223

2324
namespace SIPSorcery.Media
@@ -121,16 +122,13 @@ public byte[] EncodeAudio(short[] pcm, AudioFormat format)
121122
}
122123
else if (format.Codec == AudioCodecsEnum.L16)
123124
{
124-
// When netstandard2.1 can be used.
125-
//return MemoryMarshal.Cast<short, byte>(pcm)
126-
127125
// Put on the wire in network byte order (big endian).
128-
return pcm.SelectMany(x => new byte[] { (byte)(x >> 8), (byte)(x) }).ToArray();
126+
return MemoryOperations.ToBigEndianBytes(pcm);
129127
}
130128
else if (format.Codec == AudioCodecsEnum.PCM_S16LE)
131129
{
132130
// Put on the wire as little endian.
133-
return pcm.SelectMany(x => new byte[] { (byte)(x), (byte)(x >> 8) }).ToArray();
131+
return MemoryOperations.ToLittleEndianBytes(pcm);
134132
}
135133
else if (format.Codec == AudioCodecsEnum.OPUS)
136134
{
@@ -148,7 +146,7 @@ public byte[] EncodeAudio(short[] pcm, AudioFormat format)
148146

149147
byte[] encodedSample = new byte[pcm.Length];
150148
int encodedLength = _opusEncoder.Encode(pcmFloat, pcmFloat.Length / format.ChannelCount, encodedSample, encodedSample.Length);
151-
return encodedSample.Take(encodedLength).ToArray();
149+
return encodedSample.AsSpan(0, encodedLength).ToArray();
152150
}
153151
else
154152
{
@@ -174,7 +172,7 @@ public short[] DecodeAudio(byte[] encodedSample, AudioFormat format)
174172
short[] decodedPcm = new short[encodedSample.Length * 2];
175173
int decodedSampleCount = _g722Decoder.Decode(_g722DecoderState, decodedPcm, encodedSample, encodedSample.Length);
176174

177-
return decodedPcm.Take(decodedSampleCount).ToArray();
175+
return decodedPcm.AsSpan(0, decodedSampleCount).ToArray();
178176
}
179177
if (format.Codec == AudioCodecsEnum.G729)
180178
{

src/app/Media/Codecs/G729Encoder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class G729Encoder : Ld8k
5555
* Initialization of the coder.
5656
*/
5757

58-
private byte[] _leftover = new byte[0];
58+
private byte[] _leftover = Array.Empty<byte>();
5959

6060
/**
6161
* Init the Ld8k Coder

src/core/SIP/Channels/SIPClientWebSocketChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ private void MonitorReceiveTasks()
376376
if (receiveTask.IsCompleted)
377377
{
378378
logger.LogDebug("Client web socket connection to {ServerUri} received {BytesReceived} bytes.", conn.ServerUri, receiveTask.Result.Count);
379-
//SIPMessageReceived(this, conn.LocalEndPoint, conn.RemoteEndPoint, conn.ReceiveBuffer.Take(receiveTask.Result.Count).ToArray()).Wait();
379+
//SIPMessageReceived(this, conn.LocalEndPoint, conn.RemoteEndPoint, conn.ReceiveBuffer.AsSpan(0, receiveTask.Result.Count).ToArray()).Wait();
380380
ExtractSIPMessages(this, conn, conn.ReceiveBuffer, receiveTask.Result.Count);
381381
conn.ReceiveTask = conn.Client.ReceiveAsync(conn.ReceiveBuffer, m_cts.Token);
382382
}

src/core/SIP/Channels/SIPWebSocketChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ protected override void OnError(ErrorEventArgs e)
122122

123123
public void Send(byte[] buffer, int offset, int length)
124124
{
125-
base.Send(buffer.Skip(offset).Take(length).ToArray());
125+
base.Send(buffer.AsSpan(offset, length).ToArray());
126126
}
127127
}
128128

src/net/DtlsSrtp/DtlsSrtpTransport.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
using System;
2020
using System.Collections.Concurrent;
21+
using System.ComponentModel;
2122
using Microsoft.Extensions.Logging;
2223
using Org.BouncyCastle.Tls;
2324
using SIPSorcery.Sys;
@@ -482,11 +483,15 @@ public int GetSendLimit()
482483
return this._sendLimit;
483484
}
484485

485-
public void WriteToRecvStream(byte[] buf)
486+
[Obsolete("Use WriteToRecvStream(ReadOnlySpan<byte>) instead.", false)]
487+
[EditorBrowsable(EditorBrowsableState.Never)]
488+
public void WriteToRecvStream(byte[] buf) => WriteToRecvStream(buf.AsSpan());
489+
490+
public void WriteToRecvStream(ReadOnlySpan<byte> buf)
486491
{
487492
if (!_isClosed)
488493
{
489-
_chunks.Add(buf);
494+
_chunks.Add(buf.ToArray());
490495
}
491496
}
492497

src/net/HEP/HepPacket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ public static byte[] GetBytes(SIPEndPoint srcEndPoint, SIPEndPoint dstEndPoint,
316316
Buffer.BlockCopy(BitConverter.GetBytes((ushort)offset), 0, packetBuffer, 4, 2);
317317
}
318318

319-
return packetBuffer.Take(offset).ToArray();
319+
return packetBuffer.AsSpan(0, offset).ToArray();
320320
}
321321
}
322322
}

src/net/RTCP/RTCPCompoundPacket.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public static bool TryParse(
298298
}
299299
else
300300
{
301-
var buffer = packet.Skip(offset).ToArray();
301+
var buffer = packet.AsSpan(offset).ToArray();
302302

303303
// The payload type field is the second byte in the RTCP header.
304304
byte packetTypeID = buffer[1];

src/net/RTCP/RTCPTWCCFeedback.cs

Lines changed: 12 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ public RTCPTWCCFeedback(ReadOnlySpan<byte> packet)
180180
packet = packet.Slice(RTCPHeader.HEADER_BYTES_LENGTH);
181181

182182
// Parse sender and media SSRCs
183-
SenderSSRC = ReadUInt32(ref packet);
184-
MediaSSRC = ReadUInt32(ref packet);
183+
SenderSSRC = BinaryOperations.ReadUInt32BigEndian(ref packet);
184+
MediaSSRC = BinaryOperations.ReadUInt32BigEndian(ref packet);
185185

186186
// Parse Base Sequence Number and Packet Status Count
187-
BaseSequenceNumber = ReadUInt16(ref packet);
188-
PacketStatusCount = ReadUInt16(ref packet);
187+
BaseSequenceNumber = BinaryOperations.ReadUInt16BigEndian(ref packet);
188+
PacketStatusCount = BinaryOperations.ReadUInt16BigEndian(ref packet);
189189

190190
// Parse Reference Time and Feedback Packet Count
191191
ReferenceTime = ParseReferenceTime(ref packet, out byte fbCount);
@@ -271,38 +271,6 @@ private uint ParseReferenceTime(ref ReadOnlySpan<byte> packet, out byte fbCount)
271271
return (uint)((b1 << 16) | (b2 << 8) | b3);
272272
}
273273

274-
private List<TWCCPacketStatusType> ParseStatusChunks(byte[] packet, ref int offset)
275-
{
276-
var statusSymbols = new List<TWCCPacketStatusType>();
277-
int remainingStatuses = PacketStatusCount;
278-
279-
while (remainingStatuses > 0)
280-
{
281-
if (offset + 2 > packet.Length)
282-
{
283-
throw new ArgumentException($"Packet truncated during status chunk parsing. Expected {remainingStatuses} more statuses.");
284-
}
285-
286-
var chunk = ReadUInt16(packet, ref offset);
287-
var chunkType = chunk >> 14;
288-
289-
switch (chunkType)
290-
{
291-
case 0: // Run Length Chunk
292-
ParseRunLengthChunk(chunk, statusSymbols, ref remainingStatuses);
293-
break;
294-
case 2: // Two-bit Status Vector
295-
ParseTwoBitStatusVector(chunk, statusSymbols, ref remainingStatuses);
296-
break;
297-
case 3: // One-bit Status Vector
298-
ParseOneBitStatusVector(chunk, statusSymbols, ref remainingStatuses);
299-
break;
300-
}
301-
}
302-
303-
return statusSymbols;
304-
}
305-
306274
private List<TWCCPacketStatusType> ParseStatusChunks(ref ReadOnlySpan<byte> packet)
307275
{
308276
var statusSymbols = new List<TWCCPacketStatusType>();
@@ -315,7 +283,7 @@ private List<TWCCPacketStatusType> ParseStatusChunks(ref ReadOnlySpan<byte> pack
315283
throw new ArgumentException($"Packet truncated during status chunk parsing. Expected {remainingStatuses} more statuses.");
316284
}
317285

318-
var chunk = ReadUInt16(ref packet);
286+
var chunk = BinaryOperations.ReadUInt16BigEndian(ref packet);
319287
var chunkType = chunk >> 14;
320288

321289
switch (chunkType)
@@ -600,15 +568,15 @@ private void WriteBytesCore(Span<byte> buffer)
600568
buffer = buffer.Slice(RTCPHeader.HEADER_BYTES_LENGTH);
601569

602570
// Write Sender and Media SSRC.
603-
WriteUInt32(ref buffer, SenderSSRC);
604-
WriteUInt32(ref buffer, MediaSSRC);
571+
BinaryOperations.WriteUInt32BigEndian(ref buffer, SenderSSRC);
572+
BinaryOperations.WriteUInt32BigEndian(ref buffer, MediaSSRC);
605573

606574
// Write Base Sequence Number and Packet Status Count.
607-
WriteUInt16(ref buffer, BaseSequenceNumber);
608-
WriteUInt16(ref buffer, PacketStatusCount);
575+
BinaryOperations.WriteUInt16BigEndian(ref buffer, BaseSequenceNumber);
576+
BinaryOperations.WriteUInt16BigEndian(ref buffer, PacketStatusCount);
609577

610578
// Build the 32-bit word for ReferenceTime and FeedbackPacketCount.
611-
WriteUInt32(ref buffer, (ReferenceTime << 8) | FeedbackPacketCount);
579+
BinaryOperations.WriteUInt32BigEndian(ref buffer, (ReferenceTime << 8) | FeedbackPacketCount);
612580

613581
for (var i = 0; i < PacketStatuses.Count;)
614582
{
@@ -646,7 +614,7 @@ private void WriteBytesCore(Span<byte> buffer)
646614

647615
var chunk = (ushort)(statusBits << 12);
648616
chunk |= (ushort)(runLength & 0x0FFF);
649-
WriteUInt16(ref buffer, chunk);
617+
BinaryOperations.WriteUInt16BigEndian(ref buffer, chunk);
650618
i += runLength;
651619
}
652620
else
@@ -677,7 +645,7 @@ private void WriteBytesCore(Span<byte> buffer)
677645

678646
chunk |= (ushort)(statusBits << (12 - 2 * j));
679647
}
680-
WriteUInt16(ref buffer, chunk);
648+
BinaryOperations.WriteUInt16BigEndian(ref buffer, chunk);
681649
i += count;
682650
}
683651
}
@@ -713,77 +681,5 @@ public override string ToString()
713681
$"StatusCount={PacketStatusCount}, RefTime={ReferenceTime} (1/64 sec), " +
714682
$"FbkPktCount={FeedbackPacketCount}, PacketStatuses=[{packetStatusInfo}]";
715683
}
716-
717-
#region Helper Methods
718-
719-
private uint ReadUInt32(byte[] buffer, ref int offset)
720-
{
721-
uint value = BitConverter.ToUInt32(buffer, offset);
722-
if (BitConverter.IsLittleEndian)
723-
{
724-
value = NetConvert.DoReverseEndian(value);
725-
}
726-
offset += 4;
727-
return value;
728-
}
729-
730-
private uint ReadUInt32(ref ReadOnlySpan<byte> buffer)
731-
{
732-
var value = BinaryPrimitives.ReadUInt32BigEndian(buffer);
733-
buffer = buffer.Slice(sizeof(uint));
734-
return value;
735-
}
736-
737-
private ushort ReadUInt16(byte[] buffer, ref int offset)
738-
{
739-
ushort value = BitConverter.ToUInt16(buffer, offset);
740-
if (BitConverter.IsLittleEndian)
741-
{
742-
value = NetConvert.DoReverseEndian(value);
743-
}
744-
offset += 2;
745-
return value;
746-
}
747-
748-
private ushort ReadUInt16(ref ReadOnlySpan<byte> buffer)
749-
{
750-
var value = BinaryPrimitives.ReadUInt16BigEndian(buffer);
751-
buffer = buffer.Slice(sizeof(ushort));
752-
return value;
753-
}
754-
755-
private void WriteUInt32(byte[] buffer, ref int offset, uint value)
756-
{
757-
if (BitConverter.IsLittleEndian)
758-
{
759-
value = NetConvert.DoReverseEndian(value);
760-
}
761-
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buffer, offset, 4);
762-
offset += 4;
763-
}
764-
765-
private void WriteUInt16(byte[] buffer, ref int offset, ushort value)
766-
{
767-
if (BitConverter.IsLittleEndian)
768-
{
769-
value = NetConvert.DoReverseEndian(value);
770-
}
771-
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buffer, offset, 2);
772-
offset += 2;
773-
}
774-
775-
private static void WriteUInt32(ref Span<byte> buffer, uint value)
776-
{
777-
BinaryPrimitives.WriteUInt32BigEndian(buffer, value);
778-
buffer = buffer.Slice(sizeof(uint));
779-
}
780-
781-
private static void WriteUInt16(ref Span<byte> buffer, ushort value)
782-
{
783-
BinaryPrimitives.WriteUInt16BigEndian(buffer, value);
784-
buffer = buffer.Slice(sizeof(ushort));
785-
}
786-
787-
#endregion
788684
}
789685
}

src/net/RTP/MediaStream.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public bool IsSecurityContextReady()
280280

281281
if (res == 0)
282282
{
283-
return (true, buffer.Take(outBufLen).ToArray());
283+
return (true, buffer.AsSpan(0, outBufLen).ToArray());
284284
}
285285
else
286286
{
@@ -467,7 +467,7 @@ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
467467
}
468468
else
469469
{
470-
rtpChannel.Send(RTPChannelSocketsEnum.RTP, DestinationEndPoint, rtpBuffer.Take(outBufLen).ToArray());
470+
rtpChannel.Send(RTPChannelSocketsEnum.RTP, DestinationEndPoint, rtpBuffer.AsSpan(0, outBufLen).ToArray());
471471
}
472472
}
473473

@@ -615,7 +615,7 @@ private bool SendRtcpReport(byte[] reportBuffer)
615615
}
616616
else
617617
{
618-
rtpChannel.Send(sendOnSocket, ControlDestinationEndPoint, sendBuffer.Take(outBufLen).ToArray());
618+
rtpChannel.Send(sendOnSocket, ControlDestinationEndPoint, sendBuffer.AsSpan(0, outBufLen).ToArray());
619619
}
620620
}
621621
}

src/net/RTP/Packetisation/H264Packetiser.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
// BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
3131
//-----------------------------------------------------------------------------
3232

33+
using System;
3334
using System.Collections.Generic;
3435
using System.Linq;
3536

@@ -74,7 +75,7 @@ public static IEnumerable<H264Nal> ParseNals(byte[] accessUnit)
7475
int nalSize = endPosn - currPosn;
7576
bool isLast = currPosn + nalSize == accessUnit.Length;
7677

77-
yield return new H264Nal(accessUnit.Skip(currPosn).Take(nalSize).ToArray(), isLast);
78+
yield return new H264Nal(accessUnit.AsSpan(currPosn, nalSize).ToArray(), isLast);
7879
}
7980

8081
currPosn = nalStart;
@@ -87,7 +88,7 @@ public static IEnumerable<H264Nal> ParseNals(byte[] accessUnit)
8788

8889
if (currPosn < accessUnit.Length)
8990
{
90-
yield return new H264Nal(accessUnit.Skip(currPosn).ToArray(), true);
91+
yield return new H264Nal(accessUnit.AsSpan(currPosn).ToArray(), true);
9192
}
9293
}
9394

0 commit comments

Comments
 (0)