Skip to content

Commit 9d136a4

Browse files
committed
perf(audio-stream): reduce memory allocation on send audio frame
1 parent 682a6f2 commit 9d136a4

File tree

7 files changed

+141
-44
lines changed

7 files changed

+141
-44
lines changed

src/app/Media/VoIPMediaSession.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,13 +338,13 @@ protected void RtpMediaPacketReceived(IPEndPoint remoteEndPoint, SDPMediaTypesEn
338338
{
339339
logger.LogTrace(nameof(RtpMediaPacketReceived) + " audio RTP packet received from {RemoteEndPoint} ssrc {SyncSource} seqnum {SequenceNumber} timestamp {Timestamp} payload type {PayloadType}.", remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType);
340340

341-
Media.AudioSink.GotAudioRtp(remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType, marker, rtpPacket.Payload);
341+
Media.AudioSink.GotAudioRtp(remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType, marker, rtpPacket.GetPayloadBytes());
342342
}
343343
else if (mediaType == SDPMediaTypesEnum.text && Media.TextSink != null)
344344
{
345345
logger.LogTrace(nameof(RtpMediaPacketReceived) + " text RTP packet received from {RemoteEndPoint} ssrc {SyncSource} seqnum {SequenceNumber} timestamp {Timestamp} payload type {PayloadType}.", remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType);
346346

347-
Media.TextSink.GotTextRtp(remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType, hdr.MarkerBit, rtpPacket.Payload);
347+
Media.TextSink.GotTextRtp(remoteEndPoint, hdr.SyncSource, hdr.SequenceNumber, hdr.Timestamp, hdr.PayloadType, hdr.MarkerBit, rtpPacket.GetPayloadBytes());
348348
}
349349
}
350350

src/net/RTCP/RTCPSession.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public void RecordRtpPacketReceived(RTPPacket rtpPacket)
234234
LastActivityAt = DateTime.Now;
235235
IsTimedOut = false;
236236
PacketsReceivedCount++;
237-
OctetsReceivedCount += (uint)rtpPacket.Payload.Length;
237+
OctetsReceivedCount += rtpPacket.GetPayloadLength();
238238

239239
if (m_receptionReport == null)
240240
{
@@ -272,7 +272,7 @@ public void RecordRtpPacketSend(RTPPacket rtpPacket)
272272
}
273273

274274
PacketsSentCount++;
275-
OctetsSentCount += (uint)rtpPacket.Payload.Length;
275+
OctetsSentCount += rtpPacket.GetPayloadLength();
276276
LastSeqNum = rtpPacket.Header.SequenceNumber;
277277
LastRtpTimestampSent = rtpPacket.Header.Timestamp;
278278
LastNtpTimestampSent = DateTimeToNtpTimestamp(DateTime.Now);

src/net/RTP/AudioStream.cs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public bool HasAudio
6161
/// <param name="durationRtpUnits">The duration in RTP timestamp units of the audio sample. This
6262
/// value is added to the previous RTP timestamp when building the RTP header.</param>
6363
/// <param name="sample">The audio sample to set as the RTP packet payload.</param>
64-
public void SendAudio(uint durationRtpUnits, byte[] sample)
64+
public void SendAudio(uint durationRtpUnits, ArraySegment<byte> sample)
6565
{
6666
if (!sendingFormatFound)
6767
{
@@ -71,14 +71,25 @@ public void SendAudio(uint durationRtpUnits, byte[] sample)
7171
SendAudioFrame(durationRtpUnits, NegotiatedFormat.ID, sample);
7272
}
7373

74+
/// <summary>
75+
/// Sends an audio sample to the remote peer.
76+
/// </summary>
77+
/// <param name="durationRtpUnits">The duration in RTP timestamp units of the audio sample. This
78+
/// value is added to the previous RTP timestamp when building the RTP header.</param>
79+
/// <param name="sample">The audio sample to set as the RTP packet payload.</param>
80+
public void SendAudio(uint durationRtpUnits, byte[] sample)
81+
{
82+
SendAudio(durationRtpUnits, new ArraySegment<byte>(sample));
83+
}
84+
7485
/// <summary>
7586
/// Sends an audio packet to the remote party.
7687
/// </summary>
7788
/// <param name="duration">The duration of the audio payload in timestamp units. This value
7889
/// gets added onto the timestamp being set in the RTP header.</param>
7990
/// <param name="payloadTypeID">The payload ID to set in the RTP header.</param>
80-
/// <param name="buffer">The audio payload to send.</param>
81-
public void SendAudioFrame(uint duration, int payloadTypeID, byte[] buffer)
91+
/// <param name="bufferSegment">The audio payload to send.</param>
92+
public void SendAudioFrame(uint duration, int payloadTypeID, ArraySegment<byte> bufferSegment)
8293
{
8394
if (CheckIfCanSendRtpRaw())
8495
{
@@ -99,29 +110,30 @@ public void SendAudioFrame(uint duration, int payloadTypeID, byte[] buffer)
99110
// See https://github.com/sipsorcery/sipsorcery/issues/394.
100111

101112
int maxPayload = RTPSession.RTP_MAX_PAYLOAD;
102-
int totalPackets = (buffer.Length + maxPayload - 1) / maxPayload;
113+
int totalPackets = (bufferSegment.Count + maxPayload - 1) / maxPayload;
103114

104115
uint totalIncrement = 0;
105116
uint startTimestamp = LocalTrack.Timestamp; // Keep track of where we started.
106117

107118
for (int index = 0; index < totalPackets; index++)
108119
{
109120
int offset = index * maxPayload;
110-
int payloadLength = Math.Min(maxPayload, buffer.Length - offset);
121+
int payloadLength = Math.Min(maxPayload, bufferSegment.Count - offset);
111122

112-
double fraction = (double)payloadLength / buffer.Length;
123+
double fraction = (double)payloadLength / bufferSegment.Count;
113124
uint packetDuration = (uint)Math.Round(fraction * duration);
114125

115-
byte[] payload = new byte[payloadLength];
116-
Buffer.BlockCopy(buffer, offset, payload, 0, payloadLength);
117-
118126
// RFC3551 specifies that for audio the marker bit should always be 0 except for when returning
119127
// from silence suppression. For video the marker bit DOES get set to 1 for the last packet
120128
// in a frame.
121129
int markerBit = 0;
122-
130+
#if NETCOREAPP2_1_OR_GREATER && !NETFRAMEWORK
131+
var memorySegment = bufferSegment.Slice(offset, payloadLength);
132+
#else
133+
var memorySegment = new ArraySegment<byte>(bufferSegment.Array!, offset, payloadLength);
134+
#endif
123135
// Send this packet at the current LocalTrack.Timestamp
124-
SendRtpRaw(payload, LocalTrack.Timestamp, markerBit, payloadTypeID, true);
136+
SendRtpRaw(memorySegment, LocalTrack.Timestamp, markerBit, payloadTypeID, true);
125137

126138
// After sending, increment the timestamp by this packet's portion.
127139
// This ensures the timestamp increments for the next packet, including the first one.
@@ -143,6 +155,18 @@ public void SendAudioFrame(uint duration, int payloadTypeID, byte[] buffer)
143155
}
144156
}
145157

158+
/// <summary>
159+
/// Sends an audio packet to the remote party.
160+
/// </summary>
161+
/// <param name="duration">The duration of the audio payload in timestamp units. This value
162+
/// gets added onto the timestamp being set in the RTP header.</param>
163+
/// <param name="payloadTypeID">The payload ID to set in the RTP header.</param>
164+
/// <param name="buffer">The audio payload to send.</param>
165+
public void SendAudioFrame(uint duration, int payloadTypeID, byte[] buffer)
166+
{
167+
SendAudioFrame(duration, payloadTypeID, new ArraySegment<byte>(buffer));
168+
}
169+
146170
/// <summary>
147171
/// Sends an RTP event for a DTMF tone as per RFC2833. Sending the event requires multiple packets to be sent.
148172
/// This method will hold onto the socket until all the packets required for the event have been sent. The send

src/net/RTP/MediaStream.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -377,14 +377,15 @@ private static byte[] Combine(params byte[][] arrays)
377377
return rv;
378378
}
379379

380-
protected void SendRtpRaw(byte[] data, uint timestamp, int markerBit, int payloadType, Boolean checkDone, ushort? seqNum = null)
380+
protected void SendRtpRaw(ArraySegment<byte> data, uint timestamp, int markerBit, int payloadType, Boolean checkDone, ushort? seqNum = null)
381381
{
382382
if (checkDone || CheckIfCanSendRtpRaw())
383383
{
384384
ProtectRtpPacket protectRtpPacket = SecureContext?.ProtectRtpPacket;
385385
int srtpProtectionLength = (protectRtpPacket != null) ? RTPSession.SRTP_MAX_PREFIX_LENGTH : 0;
386386

387-
RTPPacket rtpPacket = new RTPPacket(data.Length + srtpProtectionLength);
387+
RTPPacket rtpPacket = new RTPPacket(data, srtpProtectionLength);
388+
388389
rtpPacket.Header.SyncSource = LocalTrack.Ssrc;
389390
rtpPacket.Header.SequenceNumber = seqNum ?? LocalTrack.GetNextSeqNum();
390391
rtpPacket.Header.Timestamp = timestamp;
@@ -452,8 +453,6 @@ 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
452453
rtpPacket.Header.HeaderExtensionFlag = 0;
453454
}
454455

455-
Buffer.BlockCopy(data, 0, rtpPacket.Payload, 0, data.Length);
456-
457456
var rtpBuffer = rtpPacket.GetBytes();
458457

459458
if (protectRtpPacket == null)
@@ -478,6 +477,11 @@ 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
478477
}
479478
}
480479

480+
protected void SendRtpRaw(byte[] data, uint timestamp, int markerBit, int payloadType, Boolean checkDone, ushort? seqNum = null)
481+
{
482+
SendRtpRaw(new ArraySegment<byte>(data), timestamp, markerBit, payloadType, checkDone, seqNum);
483+
}
484+
481485
/// <summary>
482486
/// To set a new value to a RTP Header extension.
483487
///
@@ -679,7 +683,7 @@ public void OnReceiveRTPPacket(RTPHeader hdr, int localPort, IPEndPoint remoteEn
679683
return;
680684
}
681685

682-
RaiseOnRtpEventByIndex(remoteEndPoint, new RTPEvent(rtpPacket.Payload), rtpPacket.Header);
686+
RaiseOnRtpEventByIndex(remoteEndPoint, new RTPEvent(rtpPacket.GetPayloadBytes()), rtpPacket.Header);
683687
return;
684688
}
685689

src/net/RTP/Packetisation/RtpVideoFramer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public RtpVideoFramer(VideoCodecsEnum codec, int maxFrameSize)
6363

6464
public byte[] GotRtpPacket(RTPPacket rtpPacket)
6565
{
66-
var payload = rtpPacket.Payload;
66+
var payload = rtpPacket.GetPayloadBytes();
6767

6868
var hdr = rtpPacket.Header;
6969

src/net/RTP/RTPPacket.cs

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,24 @@
1515
//-----------------------------------------------------------------------------
1616

1717
using System;
18+
#if !NETCOREAPP2_1_OR_GREATER || NETFRAMEWORK
19+
using System.Linq;
20+
#endif
1821

1922
namespace SIPSorcery.Net
2023
{
2124
public class RTPPacket
2225
{
2326
public RTPHeader Header;
24-
public byte[] Payload;
27+
private byte[] _payload;
28+
private ArraySegment<byte> _payloadSegment;
29+
private int _srtpProtectionLength = 0;
30+
31+
public byte[] Payload
32+
{
33+
get { return _payload; }
34+
set { _payload = value; }
35+
}
2536

2637
public RTPPacket()
2738
{
@@ -31,23 +42,81 @@ public RTPPacket()
3142
public RTPPacket(int payloadSize)
3243
{
3344
Header = new RTPHeader();
34-
Payload = new byte[payloadSize];
45+
_payload = new byte[payloadSize];
3546
}
3647

3748
public RTPPacket(byte[] packet)
3849
{
3950
Header = new RTPHeader(packet);
40-
Payload = new byte[Header.PayloadSize];
41-
Array.Copy(packet, Header.Length, Payload, 0, Payload.Length);
51+
_payload = new byte[Header.PayloadSize];
52+
Array.Copy(packet, Header.Length, _payload, 0, _payload.Length);
53+
}
54+
55+
public RTPPacket(ArraySegment<byte> packet, int srtpProtectionLength)
56+
{
57+
Header = new RTPHeader();
58+
_payloadSegment = packet;
59+
_srtpProtectionLength = srtpProtectionLength;
60+
}
61+
62+
public uint GetPayloadLength()
63+
{
64+
return (uint)(_payload?.Length ?? _payloadSegment.Count);
65+
}
66+
67+
public byte[] GetPayloadBytes()
68+
{
69+
Payload ??= _payloadSegment.ToArray();
70+
71+
return Payload;
72+
}
73+
74+
public byte GetPayloadByteAt(int index)
75+
{
76+
#if NETCOREAPP2_1_OR_GREATER && !NETFRAMEWORK
77+
return _payload?[index] ?? _payloadSegment[index];
78+
#else
79+
return _payload?[index] ?? _payloadSegment.ElementAt(index);
80+
#endif
81+
}
82+
83+
public ArraySegment<byte> GetPayloadSegment(int offset, int length)
84+
{
85+
if (_payload != null)
86+
{
87+
return new ArraySegment<byte>(_payload, offset, length);
88+
}
89+
90+
#if NETCOREAPP2_1_OR_GREATER && !NETFRAMEWORK
91+
return _payloadSegment.Slice(offset, length);
92+
#else
93+
return new ArraySegment<byte>(_payloadSegment.Array!, offset + _payloadSegment.Offset, length);
94+
#endif
4295
}
4396

4497
public byte[] GetBytes()
4598
{
4699
byte[] header = Header.GetBytes();
47-
byte[] packet = new byte[header.Length + Payload.Length];
100+
byte[] packet = new byte[header.Length + (_payload?.Length ?? _payloadSegment.Count) + _srtpProtectionLength];
48101

49102
Array.Copy(header, packet, header.Length);
50-
Array.Copy(Payload, 0, packet, header.Length, Payload.Length);
103+
104+
if (_payloadSegment != null)
105+
{
106+
#if NETCOREAPP2_1_OR_GREATER && !NETFRAMEWORK
107+
_payloadSegment.CopyTo(packet, header.Length);
108+
#else
109+
Array.Copy(_payloadSegment.Array!, _payloadSegment.Offset, packet, header.Length, _payloadSegment.Count);
110+
#endif
111+
}
112+
else if (_payload != null)
113+
{
114+
Array.Copy(_payload, 0, packet, header.Length, _payload.Length);
115+
}
116+
else
117+
{
118+
throw new ApplicationException("Either _payloadSegment or _payload should be defined");
119+
}
51120

52121
return packet;
53122
}
@@ -66,15 +135,15 @@ private byte[] GetNullPayload(int numBytes)
66135

67136
public static bool TryParse(
68137
ReadOnlySpan<byte> buffer,
69-
RTPPacket packet,
138+
RTPPacket packet,
70139
out int consumed)
71140
{
72141
consumed = 0;
73142
if (RTPHeader.TryParse(buffer, out var header, out var headerConsumed))
74143
{
75144
packet.Header = header;
76145
consumed += headerConsumed;
77-
packet.Payload = buffer.Slice(headerConsumed, header.PayloadSize).ToArray();
146+
packet._payload = buffer.Slice(headerConsumed, header.PayloadSize).ToArray();
78147
consumed += header.PayloadSize;
79148
return true;
80149
}
@@ -91,4 +160,4 @@ public static bool TryParse(
91160
return TryParse(buffer, packet, out consumed);
92161
}
93162
}
94-
}
163+
}

0 commit comments

Comments
 (0)