Skip to content

Commit 4b8ef2c

Browse files
committed
Fix bug in TWCC (swap small delta and large delta)
Fix bug in absolute send-time extension (use 24 bits for time) Add support for multiple URIs per extension and make sdp answer match
1 parent f17be10 commit 4b8ef2c

7 files changed

Lines changed: 162 additions & 167 deletions

File tree

src/net/RTCP/RTCPTWCCFeedback.cs

Lines changed: 73 additions & 132 deletions
Large diffs are not rendered by default.

src/net/RTP/RTPHeaderExtensions/AbsSendTimeExtension.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ namespace SIPSorcery.Net
66
// AbsSendTimeExtension is a extension payload format in
77
// http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
88
// Code reference: https://chromium.googlesource.com/external/webrtc/+/e2a017725570ead5946a4ca8235af27470ca0df9/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc#19
9-
public class AbsSendTimeExtension: RTPHeaderExtension
9+
public class AbsSendTimeExtension : RTPHeaderExtension
1010
{
1111
public const string RTP_HEADER_EXTENSION_URI = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time";
12+
public static readonly string[] SUPPORTED_URIS =
13+
{
14+
RTP_HEADER_EXTENSION_URI,
15+
"urn:ietf:params:rtp-hdrext:sdes:abs-send-time"
16+
};
17+
1218
internal const int RTP_HEADER_EXTENSION_SIZE = 3;
1319

14-
public AbsSendTimeExtension(int id): base(id, RTP_HEADER_EXTENSION_URI, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte)
20+
public AbsSendTimeExtension(int id) : base(id, RTP_HEADER_EXTENSION_URI, SUPPORTED_URIS, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte)
1521
{
1622
}
1723

@@ -41,24 +47,41 @@ internal static byte[] AbsSendTime(int id, int extensionSize, DateTimeOffset now
4147
(byte)(abs & 0xffUL)
4248
};
4349
}
44-
50+
4551
public override byte[] Marshal()
4652
{
4753
return AbsSendTime(Id, ExtensionSize, DateTimeOffset.Now);
4854
}
4955

56+
5057
public override Object Unmarshal(RTPHeader header, byte[] data)
5158
{
52-
var ntpTimestamp = GetUlong(data);
53-
return new TimestampPair() { NtpTimestamp = ntpTimestamp.HasValue ? ntpTimestamp.Value : 0, RtpTimestamp = header.Timestamp };
59+
// Check for the correct payload size
60+
if (data == null || data.Length != RTP_HEADER_EXTENSION_SIZE)
61+
{
62+
return null;
63+
}
64+
65+
// Combine the 3 bytes into a 64-bit value for calculation
66+
ulong receivedAbsSendTime = ((ulong)data[0] << 16) | ((ulong)data[1] << 8) | (ulong)data[2];
67+
68+
// The receivedAbsSendTime is the 24-bit value from the sender.
69+
// The receiver's job is to use this value to get an estimated
70+
// NTP timestamp for synchronization. This is often done by taking
71+
// the local NTP time and replacing its high bits with the received
72+
// 24-bit value.
73+
74+
// For simplicity, let's just return the value as part of the TimestampPair.
75+
// The actual synchronization logic would be handled by the caller.
76+
return new TimestampPair() { NtpTimestamp = receivedAbsSendTime, RtpTimestamp = header.Timestamp };
5477
}
5578

5679
// DateTimeOffset.UnixEpoch only available in newer target frameworks
5780
private static readonly DateTimeOffset UnixEpoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
5881

5982
private ulong? GetUlong(byte[] data)
6083
{
61-
if ( (data.Length != ExtensionSize) || ((sizeof(ulong) - 1) > data.Length) )
84+
if ((data.Length != ExtensionSize) || ((sizeof(ulong) - 1) > data.Length))
6285
{
6386
return null;
6487
}

src/net/RTP/RTPHeaderExtensions/AudioLevelExtension.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@ public AudioLevel(byte[] data)
3434
};
3535

3636
public const string RTP_HEADER_EXTENSION_URI = "urn:ietf:params:rtp-hdrext:ssrc-audio-level";
37+
public static readonly string[] SUPPORTED_URIS =
38+
{
39+
RTP_HEADER_EXTENSION_URI
40+
};
41+
3742
internal const int RTP_HEADER_EXTENSION_SIZE = 1;
3843

3944
private AudioLevel _audioLevel;
4045

41-
public AudioLevelExtension(int id) : base(id, RTP_HEADER_EXTENSION_URI, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte, Net.SDPMediaTypesEnum.audio)
46+
public AudioLevelExtension(int id) : base(id, RTP_HEADER_EXTENSION_URI, SUPPORTED_URIS, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte, Net.SDPMediaTypesEnum.audio)
4247
{
4348
_audioLevel = new AudioLevel()
4449
{

src/net/RTP/RTPHeaderExtensions/CVOExtension.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,24 @@ public CVO(byte cvo_byte)
5252
}
5353

5454
public const string RTP_HEADER_EXTENSION_URI = "urn:3gpp:video-orientation";
55+
56+
public static readonly string[] SUPPORTED_URIS =
57+
{
58+
RTP_HEADER_EXTENSION_URI
59+
};
60+
5561
internal const int RTP_HEADER_EXTENSION_SIZE = 1;
5662

5763
private byte _cvo_byte;
5864
private CVO _cvo;
5965

60-
public CVOExtension(int id) : base(id, RTP_HEADER_EXTENSION_URI, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte, Net.SDPMediaTypesEnum.video)
66+
public CVOExtension(int id) : base(id, RTP_HEADER_EXTENSION_URI, SUPPORTED_URIS, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte, Net.SDPMediaTypesEnum.video)
6167
{
6268
_cvo_byte = 0;
6369
_cvo = new CVO();
6470
}
6571

72+
6673
/// <summary>
6774
/// To set video rotation
6875
/// </summary>

src/net/RTP/RTPHeaderExtensions/RTPHeaderExtension.cs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,56 @@ public abstract class RTPHeaderExtension
1717
public static RTPHeaderExtension GetRTPHeaderExtension(int id, string uri, SDPMediaTypesEnum media)
1818
{
1919
RTPHeaderExtension result = null;
20-
switch (uri)
20+
if (AbsSendTimeExtension.SUPPORTED_URIS.Contains(uri, StringComparer.InvariantCultureIgnoreCase))
2121
{
22-
case AbsSendTimeExtension.RTP_HEADER_EXTENSION_URI:
23-
result = new AbsSendTimeExtension(id);
24-
break;
25-
26-
case CVOExtension.RTP_HEADER_EXTENSION_URI:
27-
result = new CVOExtension(id);
28-
break;
29-
30-
case AudioLevelExtension.RTP_HEADER_EXTENSION_URI:
31-
result = new AudioLevelExtension(id);
32-
break;
33-
34-
case TransportWideCCExtension.RTP_HEADER_EXTENSION_URI:
35-
//case TransportWideCCExtension.RTP_HEADER_EXTENSION_URI_ALT:
36-
result = new TransportWideCCExtension(id);
37-
break;
22+
result = new AbsSendTimeExtension(id);
23+
}
24+
else if (CVOExtension.SUPPORTED_URIS.Contains(uri, StringComparer.InvariantCultureIgnoreCase))
25+
{
26+
result = new CVOExtension(id);
27+
}
28+
else if (AudioLevelExtension.SUPPORTED_URIS.Contains(uri, StringComparer.InvariantCultureIgnoreCase))
29+
{
30+
result = new AudioLevelExtension(id);
31+
}
32+
else if (TransportWideCCExtension.SUPPORTED_URIS.Contains(uri, StringComparer.InvariantCultureIgnoreCase))
33+
{
34+
result = new TransportWideCCExtension(id);
3835
}
3936

40-
if ( (result != null) && result.IsMediaSupported(media) )
37+
if ((result != null) && result.IsMediaSupported(media))
4138
{
39+
result.Uri = uri;
4240
return result;
4341
}
4442

4543
return null;
4644
}
4745

46+
/// <summary>
47+
/// Returns true if the URI is supported by this extension
48+
/// </summary>
49+
/// <param name="uri"></param>
50+
/// <returns></returns>
51+
public bool SupportsExtension(string uri)
52+
{
53+
return SupportedURIs.Contains(uri, StringComparer.InvariantCultureIgnoreCase);
54+
}
55+
4856
/// <summary>
4957
/// To create a RTP Header Extension
5058
/// </summary>
5159
/// <param name="id"><see cref="int"/> Id / extmap</param>
5260
/// <param name="uri"><see cref="String"/>uri</param>
61+
/// <param name="supportedURIs"><see cref="String"/> An array of supported RTP Header Extension URIs.</param>
5362
/// <param name="type"><see cref="RTPHeaderExtension"/>type (one or two bytes)</param>
5463
/// <param name="medias"><see cref="SDPMediaTypesEnum"/>media(s) supported by this extension - set null/empty if all medias are supported</param>
55-
public RTPHeaderExtension(int id, string uri, int extensionSize, RTPHeaderExtensionType type, params SDPMediaTypesEnum[] medias )
64+
public RTPHeaderExtension(int id, string uri, string[] supportedURIs, int extensionSize, RTPHeaderExtensionType type, params SDPMediaTypesEnum[] medias)
5665
{
5766
Id = id;
5867
Uri = uri;
5968
ExtensionSize = extensionSize;
69+
SupportedURIs = supportedURIs;
6070
Type = type;
6171

6272
if (medias != null)
@@ -73,7 +83,10 @@ public RTPHeaderExtension(int id, string uri, int extensionSize, RTPHeaderExtens
7383
public int Id { get; internal set; }
7484

7585
// Uri
76-
public string Uri { get; }
86+
public string Uri { get; set; }
87+
88+
//Supported URIs for this extension
89+
public string[] SupportedURIs { get; protected set; }
7790

7891
public int ExtensionSize { get; }
7992

src/net/RTP/RTPHeaderExtensions/TransportWideCCExtension.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* It provides functionality to marshal and unmarshal the TWCC header extension.
99
*
1010
* Author: Sean Tearney
11-
* Date: 2025-02-22
11+
* Date: 2025-09-08
1212
*
1313
* License: BSD 3-Clause "New" or "Revised" License, see included LICENSE.md file.
1414
*
@@ -29,11 +29,15 @@ namespace SIPSorcery.Net
2929
/// </summary>
3030
public class TransportWideCCExtension : RTPHeaderExtension
3131
{
32-
//
33-
3432
public const string RTP_HEADER_EXTENSION_URI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01";
35-
//public const string RTP_HEADER_EXTENSION_URI_ALT = "http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02";
3633

34+
public static readonly string[] SUPPORTED_URIS =
35+
{
36+
RTP_HEADER_EXTENSION_URI,
37+
"urn:ietf:params:rtp-hdrext:transport-wide-cc",
38+
"http://www.webrtc.org/experiments/rtp-hdrext/transport-wide-cc-02"
39+
40+
};
3741

3842
internal const int RTP_HEADER_EXTENSION_SIZE = 2; // TWCC payload: 2 bytes for sequence number.
3943

@@ -47,7 +51,7 @@ public class TransportWideCCExtension : RTPHeaderExtension
4751
/// </summary>
4852
/// <param name="id">The negotiated header extension id.</param>
4953
public TransportWideCCExtension(int id)
50-
: base(id, RTP_HEADER_EXTENSION_URI, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte)
54+
: base(id, RTP_HEADER_EXTENSION_URI, SUPPORTED_URIS, RTP_HEADER_EXTENSION_SIZE, RTPHeaderExtensionType.OneByte)
5155
{
5256
}
5357

src/net/WebRTC/RTCPeerConnection.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,11 +996,12 @@ public RTCSessionDescriptionInit createAnswer(RTCAnswerOptions options = null)
996996
{
997997
foreach (var remoteExtension in remoteHeaderExtensions)
998998
{
999-
var localExtension = localHeaderExtensions.FirstOrDefault(ext => ext.Uri == remoteExtension.Uri);
999+
var localExtension = localHeaderExtensions.FirstOrDefault(ext => ext.SupportsExtension(remoteExtension.Uri));
10001000
if ((localExtension != null) && _rtpExtensionsUsed.ContainsKey(remoteExtension.Uri))
10011001
{
10021002
// We must ensure to use same Id by extension
10031003
localExtension.Id = _rtpExtensionsUsed[remoteExtension.Uri];
1004+
localExtension.Uri = remoteExtension.Uri; // Keep same Uri as remote
10041005

10051006
logger.LogDebug("[createAnswer] - {Media}:[{MediaID}] - Add HeaderExtensions:[{Id} - {Uri}]", ann.Media, ann.MediaID, localExtension.Id, localExtension.Uri);
10061007
ann.HeaderExtensions.Add(localExtension.Id, localExtension);
@@ -1020,11 +1021,12 @@ public RTCSessionDescriptionInit createAnswer(RTCAnswerOptions options = null)
10201021
{
10211022
foreach (var remoteExtension in remoteHeaderExtensions)
10221023
{
1023-
var localExtension = localHeaderExtensions.FirstOrDefault(ext => ext.Uri == remoteExtension.Uri);
1024+
var localExtension = localHeaderExtensions.FirstOrDefault(ext => ext.SupportsExtension(remoteExtension.Uri));
10241025
if ((localExtension != null) && _rtpExtensionsUsed.ContainsKey(remoteExtension.Uri))
10251026
{
10261027
// We must ensure to use same Id by extension
10271028
localExtension.Id = _rtpExtensionsUsed[remoteExtension.Uri];
1029+
localExtension.Uri = remoteExtension.Uri; // Keep same Uri as remote
10281030

10291031
logger.LogDebug("[createAnswer] - {Media}:[{MediaID}] - Add HeaderExtensions:[{Id} - {Uri}]", ann.Media, ann.MediaID, localExtension.Id, localExtension.Uri);
10301032
ann.HeaderExtensions.Add(localExtension.Id, localExtension);

0 commit comments

Comments
 (0)