mediasoup 3.21.0 → 3.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +1882 -0
  2. package/node/lib/test/test-Consumer.js +111 -0
  3. package/node/lib/test/test-Producer.js +54 -0
  4. package/npm-scripts.mjs +0 -5
  5. package/package.json +9 -8
  6. package/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp +4 -6
  7. package/worker/include/RTC/NEW_RTCP/packet/ByePacket.hpp +165 -0
  8. package/worker/include/RTC/NEW_RTCP/packet/CompoundPacket.hpp +183 -0
  9. package/worker/include/RTC/NEW_RTCP/packet/Packet.hpp +354 -0
  10. package/worker/include/RTC/NEW_RTCP/packet/TODO_NEW_RTCP.md +17 -0
  11. package/worker/include/RTC/RTP/Codecs/Tools.hpp +2 -0
  12. package/worker/include/RTC/Router.hpp +4 -4
  13. package/worker/include/RTC/SCTP/association/Capabilities.hpp +73 -0
  14. package/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp +7 -9
  15. package/worker/include/RTC/SCTP/association/StateCookie.hpp +38 -28
  16. package/worker/include/RTC/SCTP/packet/Chunk.hpp +11 -7
  17. package/worker/include/RTC/SCTP/packet/ErrorCause.hpp +11 -4
  18. package/worker/include/RTC/SCTP/packet/Packet.hpp +1 -1
  19. package/worker/include/RTC/SCTP/packet/Parameter.hpp +11 -4
  20. package/worker/include/RTC/SCTP/packet/TLV.hpp +2 -2
  21. package/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +2 -2
  22. package/worker/include/RTC/Transport.hpp +10 -10
  23. package/worker/include/Worker.hpp +4 -4
  24. package/worker/meson.build +7 -0
  25. package/worker/scripts/clang-scripts.mjs +54 -0
  26. package/worker/src/RTC/Consumer.cpp +22 -4
  27. package/worker/src/RTC/NEW_RTCP/packet/ByePacket.cpp +266 -0
  28. package/worker/src/RTC/NEW_RTCP/packet/CompoundPacket.cpp +219 -0
  29. package/worker/src/RTC/NEW_RTCP/packet/Packet.cpp +222 -0
  30. package/worker/src/RTC/RTP/Codecs/H264.cpp +3 -0
  31. package/worker/src/RTC/RTP/Codecs/VP8.cpp +3 -0
  32. package/worker/src/RTC/Router.cpp +14 -14
  33. package/worker/src/RTC/SCTP/association/Association.cpp +23 -11
  34. package/worker/src/RTC/SCTP/association/Capabilities.cpp +87 -0
  35. package/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp +20 -40
  36. package/worker/src/RTC/SCTP/association/StateCookie.cpp +54 -32
  37. package/worker/src/RTC/SCTP/packet/Chunk.cpp +18 -18
  38. package/worker/src/RTC/SCTP/packet/ErrorCause.cpp +18 -18
  39. package/worker/src/RTC/SCTP/packet/Packet.cpp +2 -0
  40. package/worker/src/RTC/SCTP/packet/Parameter.cpp +18 -18
  41. package/worker/src/RTC/SCTP/packet/TLV.cpp +27 -25
  42. package/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +2 -2
  43. package/worker/src/RTC/SvcProducerStreamManager.cpp +4 -1
  44. package/worker/src/RTC/Transport.cpp +42 -30
  45. package/worker/src/Worker.cpp +10 -10
  46. package/worker/test/include/RTC/ICE/iceCommon.hpp +1 -1
  47. package/worker/test/include/RTC/RTCP/rtcpCommon.hpp +58 -0
  48. package/worker/test/include/RTC/RTP/rtpCommon.hpp +1 -1
  49. package/worker/test/src/RTC/NEW_RTCP/packet/TestByePacket.cpp +302 -0
  50. package/worker/test/src/RTC/NEW_RTCP/rtcpCommon.cpp +29 -0
  51. package/worker/test/src/RTC/RTP/TestPacket.cpp +17 -15
  52. package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +3 -3
  53. package/worker/test/src/RTC/SCTP/association/TestCapabilities.cpp +99 -0
  54. package/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp +14 -16
  55. package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +198 -102
  56. package/worker/test/src/RTC/SCTP/packet/parameters/TestStateCookieParameter.cpp +11 -10
@@ -1022,6 +1022,117 @@ test('transport.consume() for a simulcast producer honors preferredLayers', asyn
1022
1022
  temporalLayer: 4,
1023
1023
  });
1024
1024
  }, 2000);
1025
+ test('transport.consume() from a single-encoding no temporal layer producer is of type simple', async () => {
1026
+ const router = await ctx.worker.createRouter({
1027
+ mediaCodecs: ctx.vpxMediaCodecs,
1028
+ });
1029
+ const transport1 = await router.createWebRtcTransport({
1030
+ listenIps: ['127.0.0.1'],
1031
+ });
1032
+ const transport2 = await router.createWebRtcTransport({
1033
+ listenIps: ['127.0.0.1'],
1034
+ });
1035
+ const producer = await transport1.produce({
1036
+ kind: 'video',
1037
+ rtpParameters: {
1038
+ codecs: [
1039
+ {
1040
+ mimeType: 'video/VP8',
1041
+ payloadType: 96,
1042
+ clockRate: 90000,
1043
+ rtcpFeedback: [
1044
+ { type: 'nack', parameter: '' },
1045
+ { type: 'nack', parameter: 'pli' },
1046
+ ],
1047
+ },
1048
+ ],
1049
+ headerExtensions: [],
1050
+ encodings: [{ ssrc: 11111111 }],
1051
+ rtcp: { cname: 'test' },
1052
+ },
1053
+ });
1054
+ expect(producer.type).toBe('simple');
1055
+ const consumer = await transport2.consume({
1056
+ producerId: producer.id,
1057
+ rtpCapabilities: ctx.vpxConsumerDeviceCapabilities,
1058
+ });
1059
+ expect(consumer.type).toBe('simple');
1060
+ }, 2000);
1061
+ test('transport.consume() from a single-encoding multiple temporal layer producer is of type svc', async () => {
1062
+ const router = await ctx.worker.createRouter({
1063
+ mediaCodecs: ctx.vpxMediaCodecs,
1064
+ });
1065
+ const transport1 = await router.createWebRtcTransport({
1066
+ listenIps: ['127.0.0.1'],
1067
+ });
1068
+ const transport2 = await router.createWebRtcTransport({
1069
+ listenIps: ['127.0.0.1'],
1070
+ });
1071
+ const producer = await transport1.produce({
1072
+ kind: 'video',
1073
+ rtpParameters: {
1074
+ codecs: [
1075
+ {
1076
+ mimeType: 'video/VP8',
1077
+ payloadType: 96,
1078
+ clockRate: 90000,
1079
+ rtcpFeedback: [
1080
+ { type: 'nack', parameter: '' },
1081
+ { type: 'nack', parameter: 'pli' },
1082
+ ],
1083
+ },
1084
+ ],
1085
+ headerExtensions: [],
1086
+ encodings: [{ ssrc: 55555555, scalabilityMode: 'L1T3' }],
1087
+ rtcp: { cname: 'test' },
1088
+ },
1089
+ });
1090
+ expect(producer.type).toBe('svc');
1091
+ const consumer = await transport2.consume({
1092
+ producerId: producer.id,
1093
+ rtpCapabilities: ctx.vpxConsumerDeviceCapabilities,
1094
+ });
1095
+ expect(consumer.type).toBe('svc');
1096
+ }, 2000);
1097
+ test('transport.consume() from a multiple-encoding producer is of type simulcast', async () => {
1098
+ const router = await ctx.worker.createRouter({
1099
+ mediaCodecs: ctx.vpxMediaCodecs,
1100
+ });
1101
+ const transport1 = await router.createWebRtcTransport({
1102
+ listenIps: ['127.0.0.1'],
1103
+ });
1104
+ const transport2 = await router.createWebRtcTransport({
1105
+ listenIps: ['127.0.0.1'],
1106
+ });
1107
+ const producer = await transport1.produce({
1108
+ kind: 'video',
1109
+ rtpParameters: {
1110
+ codecs: [
1111
+ {
1112
+ mimeType: 'video/VP8',
1113
+ payloadType: 96,
1114
+ clockRate: 90000,
1115
+ rtcpFeedback: [
1116
+ { type: 'nack', parameter: '' },
1117
+ { type: 'nack', parameter: 'pli' },
1118
+ ],
1119
+ },
1120
+ ],
1121
+ headerExtensions: [],
1122
+ encodings: [
1123
+ { ssrc: 11111111, scalabilityMode: 'L1T3' },
1124
+ { ssrc: 22222222, scalabilityMode: 'L1T3' },
1125
+ ],
1126
+ rtcp: { cname: 'test' },
1127
+ },
1128
+ });
1129
+ expect(producer.type).toBe('simulcast');
1130
+ const consumer = await transport2.consume({
1131
+ producerId: producer.id,
1132
+ rtpCapabilities: ctx.vpxConsumerDeviceCapabilities,
1133
+ });
1134
+ expect(consumer.type).toBe('simulcast');
1135
+ }, 2000);
1025
1136
  test('consumer.setPriority() succeed', async () => {
1026
1137
  const videoConsumer = await ctx.webRtcTransport2.consume({
1027
1138
  producerId: ctx.videoProducer.id,
@@ -442,6 +442,60 @@ test('transport.produce() with no MID and with single encoding without RID or SS
442
442
  },
443
443
  })).rejects.toThrow(Error);
444
444
  }, 2000);
445
+ test('transport.produce() with single encoding and no temporal layers is of type simple', async () => {
446
+ const producer = await ctx.webRtcTransport1.produce({
447
+ kind: 'video',
448
+ rtpParameters: {
449
+ codecs: [
450
+ {
451
+ mimeType: 'video/VP8',
452
+ payloadType: 96,
453
+ clockRate: 90000,
454
+ },
455
+ ],
456
+ encodings: [{ ssrc: 11111111 }],
457
+ rtcp: { cname: 'test' },
458
+ },
459
+ });
460
+ expect(producer.type).toBe('simple');
461
+ }, 2000);
462
+ test('transport.produce() with single encoding and temporal layers is of type svc', async () => {
463
+ const producer = await ctx.webRtcTransport1.produce({
464
+ kind: 'video',
465
+ rtpParameters: {
466
+ codecs: [
467
+ {
468
+ mimeType: 'video/VP8',
469
+ payloadType: 96,
470
+ clockRate: 90000,
471
+ },
472
+ ],
473
+ encodings: [{ ssrc: 11111111, scalabilityMode: 'L1T3' }],
474
+ rtcp: { cname: 'test' },
475
+ },
476
+ });
477
+ expect(producer.type).toBe('svc');
478
+ }, 2000);
479
+ test('transport.produce() with multiple encodings is of type simulcast', async () => {
480
+ const producer = await ctx.webRtcTransport1.produce({
481
+ kind: 'video',
482
+ rtpParameters: {
483
+ codecs: [
484
+ {
485
+ mimeType: 'video/VP8',
486
+ payloadType: 96,
487
+ clockRate: 90000,
488
+ },
489
+ ],
490
+ encodings: [
491
+ { ssrc: 11111111, scalabilityMode: 'L1T3' },
492
+ { ssrc: 22222222, scalabilityMode: 'L1T3' },
493
+ ],
494
+ rtcp: { cname: 'test' },
495
+ },
496
+ });
497
+ expect(producer.type).toBe('simulcast');
498
+ }, 2000);
445
499
  test('producer.dump() succeeds', async () => {
446
500
  const audioProducer = await ctx.webRtcTransport1.produce(ctx.audioProducerOptions);
447
501
  const dump1 = await audioProducer.dump();
package/npm-scripts.mjs CHANGED
@@ -19,7 +19,6 @@ const WORKER_RELEASE_BIN = IS_WINDOWS
19
19
  : 'mediasoup-worker';
20
20
  const WORKER_RELEASE_BIN_PATH = `${WORKER_RELEASE_DIR}/${WORKER_RELEASE_BIN}`;
21
21
  const WORKER_PREBUILD_DIR = 'worker/prebuild';
22
-
23
22
  // Paths for ESLint to check.
24
23
  const ESLINT_PATHS = [
25
24
  'eslint.config.mjs',
@@ -30,10 +29,8 @@ const ESLINT_PATHS = [
30
29
  'rust-scripts.mjs',
31
30
  'worker/scripts',
32
31
  ];
33
-
34
32
  // Paths for ESLint to ignore.
35
33
  const ESLINT_IGNORE_PATHS = ['node/src/fbs'];
36
-
37
34
  // Paths for Prettier to check/write.
38
35
  // NOTE: Prettier ignores paths in .gitignore so we don't need to care about
39
36
  // node/src/fbs.
@@ -457,7 +454,6 @@ async function flatcNode({ force }) {
457
454
  })
458
455
  );
459
456
  const flatbuffersDir = flatbuffersWrap['wrap-file']['directory'];
460
-
461
457
  const flatc = path.resolve(
462
458
  path.join(
463
459
  'worker',
@@ -709,7 +705,6 @@ async function downloadPrebuiltWorker() {
709
705
  `${pkg.repository.url
710
706
  .replace(/^git\+/, '')
711
707
  .replace(/\.git$/, '')}/releases/download`;
712
-
713
708
  const workerPrebuildTar = getWorkerPrebuildTarName();
714
709
  const workerPrebuildTarUrl = `${releaseBase}/${pkg.version}/${workerPrebuildTar}`;
715
710
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasoup",
3
- "version": "3.21.0",
3
+ "version": "3.21.2",
4
4
  "description": "Cutting Edge WebRTC Video Conferencing",
5
5
  "contributors": [
6
6
  "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
@@ -42,6 +42,7 @@
42
42
  }
43
43
  },
44
44
  "files": [
45
+ "CHANGELOG.md",
45
46
  "LICENSE",
46
47
  "README.md",
47
48
  "node/lib",
@@ -113,29 +114,29 @@
113
114
  "h264-profile-level-id": "^2.3.3",
114
115
  "node-fetch": "^3.3.2",
115
116
  "supports-color": "^10.2.2",
116
- "tar": "^7.5.19"
117
+ "tar": "^7.5.20"
117
118
  },
118
119
  "devDependencies": {
119
120
  "@eslint/js": "^10.0.1",
120
121
  "@types/debug": "^4.1.13",
121
122
  "@types/ini": "^4.1.1",
122
123
  "@types/jest": "^30.0.0",
123
- "@types/node": "^26.1.0",
124
- "eslint": "^10.6.0",
124
+ "@types/node": "^26.1.1",
125
+ "eslint": "^10.7.0",
125
126
  "eslint-config-prettier": "^10.1.8",
126
127
  "eslint-plugin-jest": "^29.15.4",
127
128
  "eslint-plugin-prettier": "^5.5.6",
128
129
  "globals": "^17.7.0",
129
130
  "ini": "^7.0.0",
130
131
  "jest": "^30.4.2",
131
- "knip": "^6.24.0",
132
- "marked": "^18.0.5",
132
+ "knip": "^6.27.0",
133
+ "marked": "^18.0.6",
133
134
  "open-cli": "^9.0.0",
134
135
  "pick-port": "^2.2.1",
135
- "prettier": "^3.9.4",
136
+ "prettier": "^3.9.5",
136
137
  "ts-jest": "^29.4.11",
137
138
  "typescript": "^6.0.3",
138
- "typescript-eslint": "^8.62.1",
139
+ "typescript-eslint": "^8.64.0",
139
140
  "werift-sctp": "^0.0.11"
140
141
  }
141
142
  }
@@ -27,9 +27,7 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len)
27
27
  {
28
28
  Utils::Byte::Set8Bytes(DataBuffer, 0, RTC::SCTP::StateCookie::Magic1);
29
29
  Utils::Byte::Set2Bytes(
30
- DataBuffer,
31
- RTC::SCTP::StateCookie::NegotiatedCapabilitiesOffset,
32
- RTC::SCTP::StateCookie::Magic2);
30
+ DataBuffer, RTC::SCTP::StateCookie::RemoteCapabilitiesOffset, RTC::SCTP::StateCookie::Magic2);
33
31
  }
34
32
  }
35
33
 
@@ -46,7 +44,7 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len)
46
44
  stateCookie->GetRemoteInitialTsn();
47
45
  stateCookie->GetRemoteAdvertisedReceiverWindowCredit();
48
46
  stateCookie->GetTieTag();
49
- stateCookie->GetNegotiatedCapabilities();
47
+ stateCookie->GetRemoteCapabilities();
50
48
 
51
49
  stateCookie->Serialize(StateCookieSerializeBuffer, len);
52
50
 
@@ -56,7 +54,7 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len)
56
54
  stateCookie->GetRemoteInitialTsn();
57
55
  stateCookie->GetRemoteAdvertisedReceiverWindowCredit();
58
56
  stateCookie->GetTieTag();
59
- stateCookie->GetNegotiatedCapabilities();
57
+ stateCookie->GetRemoteCapabilities();
60
58
 
61
59
  auto* clonedStateCookie = stateCookie->Clone(StateCookieCloneBuffer, len);
62
60
 
@@ -68,7 +66,7 @@ void FuzzerRtcSctpStateCookie::Fuzz(const uint8_t* data, size_t len)
68
66
  clonedStateCookie->GetRemoteInitialTsn();
69
67
  clonedStateCookie->GetRemoteAdvertisedReceiverWindowCredit();
70
68
  clonedStateCookie->GetTieTag();
71
- clonedStateCookie->GetNegotiatedCapabilities();
69
+ clonedStateCookie->GetRemoteCapabilities();
72
70
 
73
71
  clonedStateCookie->Serialize(StateCookieSerializeBuffer, len);
74
72
 
@@ -0,0 +1,165 @@
1
+ #ifndef MS_RTC_NEW_RTCP_BYE_PACKET_HPP
2
+ #define MS_RTC_NEW_RTCP_BYE_PACKET_HPP
3
+
4
+ #include "common.hpp"
5
+ #include "RTC/NEW_RTCP/packet/Packet.hpp"
6
+ #include "Utils.hpp"
7
+ #include <string_view>
8
+ #include <vector>
9
+
10
+ namespace RTC
11
+ {
12
+ namespace NEW_RTCP
13
+ {
14
+ /**
15
+ * BYE RTCP Packet (203)
16
+ *
17
+ * @see RFC 3550.
18
+ *
19
+ * 0 1 2 3
20
+ * 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
21
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22
+ * |V=2|P| SC | PT=BYE=203 | Length |
23
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
24
+ * | SSRC/CSRC |
25
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26
+ * : ... :
27
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
28
+ * | Reason length | Reason for leaving ...
29
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30
+ *
31
+ * - Packet type (8 bits): 203.
32
+ * - Source count (SC) (5 bits): The number of SSRC/CSRC identifiers
33
+ * included in this BYE packet.
34
+ * - Reason length (8 bits): Number of bytes of text indicating the reason
35
+ * for leaving.
36
+ * - Reason for leaving (variable length): If the string fills the packet
37
+ * to the next 32-bit boundary, the string is not null terminated. If not,
38
+ * the BYE packet MUST be padded with null octets to the next 32-bit
39
+ * boundary.
40
+ */
41
+
42
+ // Forward declaration.
43
+ class Packet;
44
+
45
+ class ByePacket : public Packet
46
+ {
47
+ // We need that compound packet calls protected and private methods in
48
+ // this class.
49
+ friend class CompoundPacket;
50
+
51
+ public:
52
+ /**
53
+ * Parse a ByePacket.
54
+ *
55
+ * @remarks
56
+ * `bufferLength` may exceed the exact length of the packet.
57
+ */
58
+ static ByePacket* Parse(const uint8_t* buffer, size_t bufferLength);
59
+
60
+ /**
61
+ * Create a ByePacket.
62
+ *
63
+ * @remarks
64
+ * `bufferLength` could be greater than the packet real length.
65
+ */
66
+ static ByePacket* Factory(uint8_t* buffer, size_t bufferLength);
67
+
68
+ private:
69
+ /**
70
+ * Parse a ByePacket.
71
+ *
72
+ * @remarks
73
+ * To be used only by `Packet::Parse()`.
74
+ */
75
+ static ByePacket* ParseStrict(const uint8_t* buffer, size_t bufferLength, size_t packetLength);
76
+
77
+ private:
78
+ /**
79
+ * Only used by Parse(), ParseStrict() and Factory() static methods.
80
+ */
81
+ ByePacket(uint8_t* buffer, size_t bufferLength);
82
+
83
+ public:
84
+ ~ByePacket() override;
85
+
86
+ void Dump(int indentation = 0) const final;
87
+
88
+ ByePacket* Clone(uint8_t* buffer, size_t bufferLength) const final;
89
+
90
+ std::vector<uint32_t> GetSsrcs() const;
91
+
92
+ void AddSsrc(uint32_t ssrc);
93
+
94
+ bool HasReason() const
95
+ {
96
+ return (GetBuffer() + GetLength()) > GetReasonLengthPointer();
97
+ }
98
+
99
+ const std::string_view GetReason() const
100
+ {
101
+ if (!HasReason())
102
+ {
103
+ return {};
104
+ }
105
+
106
+ const uint8_t reasonLength = GetReasonLength();
107
+ const auto* reason = GetReasonPointer();
108
+
109
+ if (!reason)
110
+ {
111
+ return {};
112
+ }
113
+
114
+ // NOTE: Validation of the Reason length field is done outside this
115
+ // method.
116
+
117
+ return std::string_view(reinterpret_cast<const char*>(reason), reasonLength);
118
+ }
119
+
120
+ void SetReason(const std::string_view& reason);
121
+
122
+ protected:
123
+ ByePacket* SoftClone(const uint8_t* buffer) const final;
124
+
125
+ private:
126
+ uint8_t* GetSsrcsPointer() const
127
+ {
128
+ return GetVariableLengthValuePointer();
129
+ }
130
+
131
+ uint32_t GetSsrcAt(uint8_t idx) const
132
+ {
133
+ return Utils::Byte::Get4Bytes(GetSsrcsPointer(), (idx * 4));
134
+ }
135
+
136
+ uint8_t* GetReasonLengthPointer() const
137
+ {
138
+ return GetVariableLengthValuePointer() + (GetCount() * 4);
139
+ }
140
+
141
+ uint8_t* GetReasonPointer() const
142
+ {
143
+ return GetReasonLengthPointer() + 1;
144
+ }
145
+
146
+ /**
147
+ * Returns the value of the Reason length field.
148
+ *
149
+ * @remarks
150
+ * - This value must be validated outside.
151
+ */
152
+ uint8_t GetReasonLength() const
153
+ {
154
+ if (!HasReason())
155
+ {
156
+ return 0;
157
+ }
158
+
159
+ return Utils::Byte::Get1Byte(GetReasonLengthPointer(), 0);
160
+ }
161
+ };
162
+ } // namespace NEW_RTCP
163
+ } // namespace RTC
164
+
165
+ #endif
@@ -0,0 +1,183 @@
1
+ #ifndef MS_RTC_NEW_RTCP_COMPOUND_PACKET_HPP
2
+ #define MS_RTC_NEW_RTCP_COMPOUND_PACKET_HPP
3
+
4
+ #include "common.hpp"
5
+ #include "RTC/NEW_RTCP/packet/Packet.hpp"
6
+ #include "RTC/Serializable.hpp"
7
+ #include <vector>
8
+
9
+ namespace RTC
10
+ {
11
+ namespace NEW_RTCP
12
+ {
13
+ /**
14
+ * RTCP Compound Packet.
15
+ *
16
+ * @see RFC 3550.
17
+ */
18
+ class CompoundPacket : public Serializable
19
+ {
20
+ public:
21
+ using PacketsIterator = typename std::vector<Packet*>::const_iterator;
22
+
23
+ /**
24
+ * Parse an RTCP compound packet.
25
+ *
26
+ * @remarks
27
+ * - `bufferLength` must be the exact length of the compound packet.
28
+ */
29
+ static CompoundPacket* Parse(const uint8_t* buffer, size_t bufferLength);
30
+
31
+ /**
32
+ * Create an RTCP compound packet.
33
+ */
34
+ static CompoundPacket* Factory(uint8_t* buffer, size_t bufferLength);
35
+
36
+ protected:
37
+ /**
38
+ * Constructor is protected because we only want to create packets
39
+ * instances via Parse() and Factory() in subclasses.
40
+ */
41
+ CompoundPacket(uint8_t* buffer, size_t bufferLength);
42
+
43
+ public:
44
+ ~CompoundPacket() override;
45
+
46
+ void Dump(int indentation = 0) const final;
47
+
48
+ void Serialize(uint8_t* buffer, size_t bufferLength) final;
49
+
50
+ CompoundPacket* Clone(uint8_t* buffer, size_t bufferLength) const final;
51
+
52
+ bool HasPackets() const
53
+ {
54
+ return this->packets.size() > 0;
55
+ }
56
+
57
+ size_t GetPacketsCount() const
58
+ {
59
+ return this->packets.size();
60
+ }
61
+
62
+ PacketsIterator PacketsBegin() const
63
+ {
64
+ return this->packets.begin();
65
+ }
66
+
67
+ PacketsIterator PacketsEnd() const
68
+ {
69
+ return this->packets.end();
70
+ }
71
+
72
+ const Packet* GetPacketAt(size_t idx) const
73
+ {
74
+ if (idx >= this->packets.size())
75
+ {
76
+ return nullptr;
77
+ }
78
+
79
+ return this->packets[idx];
80
+ }
81
+
82
+ template<typename T>
83
+ const T* GetFirstPacketOfType() const
84
+ {
85
+ for (const auto* packet : this->packets)
86
+ {
87
+ if (typeid(*packet) == typeid(T))
88
+ {
89
+ return static_cast<const T*>(packet);
90
+ }
91
+ }
92
+
93
+ return nullptr;
94
+ }
95
+
96
+ /**
97
+ * Clone given packet into compound packet's buffer.
98
+ *
99
+ * @remarks
100
+ * - Once this method is called, the caller may want to free the original
101
+ * given packet (otherwise it will leak since the compound packet manages
102
+ * a clone of it).
103
+ *
104
+ * @throw
105
+ * - MediaSoupError - If `BuildPacketInPlace()` was called before and the
106
+ * caller didn't invoke `Consolidate()` on the returned packet yet.
107
+ */
108
+ void AddPacket(const Packet* packet);
109
+
110
+ /**
111
+ * Build a packet within the compound packet's buffer and append it to the
112
+ * list of packets. The caller can perform modifications in that packet
113
+ * and those will affect the compound packet body where the packet is
114
+ * serialzed. The desired packet class type is given via template argument.
115
+ *
116
+ * @returns Pointer of the created packet specific class.
117
+ *
118
+ * @throw
119
+ * - MediaSoupError - If `BuildPacketInPlace()` was called before and the
120
+ * caller didn't invoke `Consolidate()` on the returned packet yet.
121
+ *
122
+ * @remarks
123
+ * - The caller MUST invoke `Consolidate()` once the packet is completed.
124
+ * - The caller MUST NOT call `BuildPacketInPlace()` while other packet is
125
+ * in progress.
126
+ * - The caller MUST NOT free the obtained packet pointer since it's now
127
+ * part of the compound packet.
128
+ * - The caller MUST free the obtained packet only in case the
129
+ * `Consolidate()` method on the packet throws.
130
+ * - Method implemented in header file due to C++ template usage.
131
+ *
132
+ * @example
133
+ * ```c++
134
+ * auto* fooPacket = compoundPacket->BuildPacketInPlace<FooPacket>();
135
+ * ```
136
+ */
137
+ template<typename T>
138
+ T* BuildPacketInPlace()
139
+ {
140
+ AssertDoesNotNeedConsolidation();
141
+
142
+ // The new packet will be added after other packet in the compound
143
+ // packet, this is, at the end of the compound packet.
144
+ const auto* ptr = const_cast<uint8_t*>(GetBuffer()) + GetLength();
145
+ // The remaining length in the buffer is the potential buffer length
146
+ // of the packet.
147
+ const size_t packetMaxBufferLength = GetBufferLength() - (ptr - GetBuffer());
148
+
149
+ auto* packet = T::Factory(ptr, packetMaxBufferLength);
150
+
151
+ // NOTE: Do not fix/update the packet buffer length since the caller
152
+ // probably wants to modify the packet.
153
+
154
+ HandleInPlacePacket(packet);
155
+
156
+ return packet;
157
+ }
158
+
159
+ /**
160
+ * Whether `BuildPacketInPlace()` was called and the caller didn't invoke
161
+ * `Consolidate()` on the returned packet yet.
162
+ */
163
+ bool NeedsConsolidation() const
164
+ {
165
+ return this->needsConsolidation;
166
+ }
167
+
168
+ private:
169
+ virtual void HandleInPlacePacket(Packet* packet) final;
170
+
171
+ virtual void AssertDoesNotNeedConsolidation() const final;
172
+
173
+ private:
174
+ // Packets.
175
+ std::vector<Packet*> packets;
176
+ // Whether `BuildPacketInPlace()` was called and the caller didn't invoke
177
+ // `Consolidate()` on the returned packet yet.
178
+ bool needsConsolidation{ false };
179
+ };
180
+ } // namespace NEW_RTCP
181
+ } // namespace RTC
182
+
183
+ #endif