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.
- package/CHANGELOG.md +1882 -0
- package/node/lib/test/test-Consumer.js +111 -0
- package/node/lib/test/test-Producer.js +54 -0
- package/npm-scripts.mjs +0 -5
- package/package.json +9 -8
- package/worker/fuzzer/src/RTC/SCTP/association/FuzzerStateCookie.cpp +4 -6
- package/worker/include/RTC/NEW_RTCP/packet/ByePacket.hpp +165 -0
- package/worker/include/RTC/NEW_RTCP/packet/CompoundPacket.hpp +183 -0
- package/worker/include/RTC/NEW_RTCP/packet/Packet.hpp +354 -0
- package/worker/include/RTC/NEW_RTCP/packet/TODO_NEW_RTCP.md +17 -0
- package/worker/include/RTC/RTP/Codecs/Tools.hpp +2 -0
- package/worker/include/RTC/Router.hpp +4 -4
- package/worker/include/RTC/SCTP/association/Capabilities.hpp +73 -0
- package/worker/include/RTC/SCTP/association/NegotiatedCapabilities.hpp +7 -9
- package/worker/include/RTC/SCTP/association/StateCookie.hpp +38 -28
- package/worker/include/RTC/SCTP/packet/Chunk.hpp +11 -7
- package/worker/include/RTC/SCTP/packet/ErrorCause.hpp +11 -4
- package/worker/include/RTC/SCTP/packet/Packet.hpp +1 -1
- package/worker/include/RTC/SCTP/packet/Parameter.hpp +11 -4
- package/worker/include/RTC/SCTP/packet/TLV.hpp +2 -2
- package/worker/include/RTC/SCTP/packet/parameters/StateCookieParameter.hpp +2 -2
- package/worker/include/RTC/Transport.hpp +10 -10
- package/worker/include/Worker.hpp +4 -4
- package/worker/meson.build +7 -0
- package/worker/scripts/clang-scripts.mjs +54 -0
- package/worker/src/RTC/Consumer.cpp +22 -4
- package/worker/src/RTC/NEW_RTCP/packet/ByePacket.cpp +266 -0
- package/worker/src/RTC/NEW_RTCP/packet/CompoundPacket.cpp +219 -0
- package/worker/src/RTC/NEW_RTCP/packet/Packet.cpp +222 -0
- package/worker/src/RTC/RTP/Codecs/H264.cpp +3 -0
- package/worker/src/RTC/RTP/Codecs/VP8.cpp +3 -0
- package/worker/src/RTC/Router.cpp +14 -14
- package/worker/src/RTC/SCTP/association/Association.cpp +23 -11
- package/worker/src/RTC/SCTP/association/Capabilities.cpp +87 -0
- package/worker/src/RTC/SCTP/association/NegotiatedCapabilities.cpp +20 -40
- package/worker/src/RTC/SCTP/association/StateCookie.cpp +54 -32
- package/worker/src/RTC/SCTP/packet/Chunk.cpp +18 -18
- package/worker/src/RTC/SCTP/packet/ErrorCause.cpp +18 -18
- package/worker/src/RTC/SCTP/packet/Packet.cpp +2 -0
- package/worker/src/RTC/SCTP/packet/Parameter.cpp +18 -18
- package/worker/src/RTC/SCTP/packet/TLV.cpp +27 -25
- package/worker/src/RTC/SCTP/packet/parameters/StateCookieParameter.cpp +2 -2
- package/worker/src/RTC/SvcProducerStreamManager.cpp +4 -1
- package/worker/src/RTC/Transport.cpp +42 -30
- package/worker/src/Worker.cpp +10 -10
- package/worker/test/include/RTC/ICE/iceCommon.hpp +1 -1
- package/worker/test/include/RTC/RTCP/rtcpCommon.hpp +58 -0
- package/worker/test/include/RTC/RTP/rtpCommon.hpp +1 -1
- package/worker/test/src/RTC/NEW_RTCP/packet/TestByePacket.cpp +302 -0
- package/worker/test/src/RTC/NEW_RTCP/rtcpCommon.cpp +29 -0
- package/worker/test/src/RTC/RTP/TestPacket.cpp +17 -15
- package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +3 -3
- package/worker/test/src/RTC/SCTP/association/TestCapabilities.cpp +99 -0
- package/worker/test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp +14 -16
- package/worker/test/src/RTC/SCTP/association/TestStateCookie.cpp +198 -102
- 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.
|
|
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.
|
|
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.
|
|
124
|
-
"eslint": "^10.
|
|
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.
|
|
132
|
-
"marked": "^18.0.
|
|
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.
|
|
136
|
+
"prettier": "^3.9.5",
|
|
136
137
|
"ts-jest": "^29.4.11",
|
|
137
138
|
"typescript": "^6.0.3",
|
|
138
|
-
"typescript-eslint": "^8.
|
|
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->
|
|
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->
|
|
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->
|
|
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
|