mediasoup 3.21.1 → 3.22.0
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 +8 -0
- package/node/lib/Router.d.ts +2 -2
- package/node/lib/Router.d.ts.map +1 -1
- package/node/lib/Router.js +18 -18
- package/node/lib/RouterTypes.d.ts +0 -4
- package/node/lib/RouterTypes.d.ts.map +1 -1
- package/node/lib/test/test-PipeTransport.js +33 -1
- package/package.json +3 -3
- package/worker/include/RTC/DataConsumer.hpp +7 -10
- package/worker/include/RTC/PipeTransport.hpp +4 -0
- package/worker/include/RTC/SCTP/public/Message.hpp +16 -0
- package/worker/include/RTC/SubchannelsCodec.hpp +76 -0
- package/worker/include/RTC/Transport.hpp +5 -1
- package/worker/meson.build +2 -0
- package/worker/scripts/package-lock.json +7 -6
- package/worker/src/RTC/DataConsumer.cpp +45 -62
- package/worker/src/RTC/Router.cpp +6 -6
- package/worker/src/RTC/SubchannelsCodec.cpp +153 -0
- package/worker/src/RTC/Transport.cpp +43 -55
- package/worker/src/Worker.cpp +2 -2
- package/worker/test/src/RTC/TestSubchannelsCodec.cpp +224 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
### NEXT
|
|
4
4
|
|
|
5
|
+
### 3.22.0
|
|
6
|
+
|
|
7
|
+
- SCTP: Encode subchannels in SCTP messages to enable subchannels mechanism when using pipe transport ([PR #1859](https://github.com/versatica/mediasoup/pull/1859)).
|
|
8
|
+
|
|
9
|
+
### 3.21.2
|
|
10
|
+
|
|
11
|
+
- Worker: Fix crash when an SCTP `DataConsumer` is closed and triggers buffered amount low event ([PR #1858](https://github.com/versatica/mediasoup/pull/1858)).
|
|
12
|
+
|
|
5
13
|
### 3.21.1
|
|
6
14
|
|
|
7
15
|
- Worker: Enable SVC for VP8 and H264 ([PR #1851](https://github.com/versatica/mediasoup/pull/1851)).
|
package/node/lib/Router.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EnhancedEventEmitter } from './enhancedEvents';
|
|
2
2
|
import type { Channel } from './Channel';
|
|
3
|
-
import type { Router, PipeToRouterOptions, PipeToRouterResult,
|
|
3
|
+
import type { Router, PipeToRouterOptions, PipeToRouterResult, RouterDump, RouterEvents, RouterObserver } from './RouterTypes';
|
|
4
4
|
import type { Transport } from './TransportTypes';
|
|
5
5
|
import type { WebRtcTransport, WebRtcTransportOptions } from './WebRtcTransportTypes';
|
|
6
6
|
import type { PlainTransport, PlainTransportOptions } from './PlainTransportTypes';
|
|
@@ -44,7 +44,6 @@ export declare class RouterImpl<RouterAppData extends AppData = AppData> extends
|
|
|
44
44
|
createPipeTransport<PipeTransportAppData extends AppData = AppData>({ listenInfo, listenIp, port, enableSctp, maxSendMessageSize, maxReceiveMessageSize, sctpSendBufferSize, sctpPerStreamSendQueueLimit, sctpMaxReceiverWindowBufferSize, enableRtx, enableSrtp, appData, }: PipeTransportOptions<PipeTransportAppData>): Promise<PipeTransport<PipeTransportAppData>>;
|
|
45
45
|
createDirectTransport<DirectTransportAppData extends AppData = AppData>({ maxSendMessageSize, maxReceiveMessageSize, appData, }?: DirectTransportOptions<DirectTransportAppData>): Promise<DirectTransport<DirectTransportAppData>>;
|
|
46
46
|
pipeToRouter({ producerId, dataProducerId, router, keepId, listenInfo, listenIp, enableSctp, maxSendMessageSize, maxReceiveMessageSize, sctpSendBufferSize, sctpPerStreamSendQueueLimit, sctpMaxReceiverWindowBufferSize, enableRtx, enableSrtp, }: PipeToRouterOptions): Promise<PipeToRouterResult>;
|
|
47
|
-
addPipeTransportPair(pipeTransportPairKey: string, pipeTransportPairPromise: Promise<PipeTransportPair>): void;
|
|
48
47
|
createActiveSpeakerObserver<ActiveSpeakerObserverAppData extends AppData = AppData>({ interval, appData, }?: ActiveSpeakerObserverOptions<ActiveSpeakerObserverAppData>): Promise<ActiveSpeakerObserver<ActiveSpeakerObserverAppData>>;
|
|
49
48
|
createAudioLevelObserver<AudioLevelObserverAppData extends AppData = AppData>({ maxEntries, threshold, interval, appData, }?: AudioLevelObserverOptions<AudioLevelObserverAppData>): Promise<AudioLevelObserver<AudioLevelObserverAppData>>;
|
|
50
49
|
canConsume({ producerId, rtpCapabilities, }: {
|
|
@@ -52,6 +51,7 @@ export declare class RouterImpl<RouterAppData extends AppData = AppData> extends
|
|
|
52
51
|
rtpCapabilities: RtpCapabilities;
|
|
53
52
|
}): boolean;
|
|
54
53
|
updateMediaCodecs(mediaCodecs: RouterRtpCodecCapability[]): void;
|
|
54
|
+
private addPipeTransportPair;
|
|
55
55
|
private handleListenerError;
|
|
56
56
|
}
|
|
57
57
|
export {};
|
package/node/lib/Router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../src/Router.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACX,MAAM,EACN,mBAAmB,EACnB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../src/Router.ts"],"names":[],"mappings":"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"}
|
package/node/lib/Router.js
CHANGED
|
@@ -694,24 +694,6 @@ class RouterImpl extends enhancedEvents_1.EnhancedEventEmitter {
|
|
|
694
694
|
throw new Error('internal error');
|
|
695
695
|
}
|
|
696
696
|
}
|
|
697
|
-
addPipeTransportPair(pipeTransportPairKey, pipeTransportPairPromise) {
|
|
698
|
-
if (this.#mapRouterPairPipeTransportPairPromise.has(pipeTransportPairKey)) {
|
|
699
|
-
throw new Error('given pipeTransportPairKey already exists in this Router');
|
|
700
|
-
}
|
|
701
|
-
this.#mapRouterPairPipeTransportPairPromise.set(pipeTransportPairKey, pipeTransportPairPromise);
|
|
702
|
-
pipeTransportPairPromise
|
|
703
|
-
.then(pipeTransportPair => {
|
|
704
|
-
const localPipeTransport = pipeTransportPair[this.id];
|
|
705
|
-
// NOTE: No need to do any other cleanup here since that is done by the
|
|
706
|
-
// Router calling this method on us.
|
|
707
|
-
localPipeTransport.observer.on('close', () => {
|
|
708
|
-
this.#mapRouterPairPipeTransportPairPromise.delete(pipeTransportPairKey);
|
|
709
|
-
});
|
|
710
|
-
})
|
|
711
|
-
.catch(() => {
|
|
712
|
-
this.#mapRouterPairPipeTransportPairPromise.delete(pipeTransportPairKey);
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
697
|
async createActiveSpeakerObserver({ interval = 300, appData, } = {}) {
|
|
716
698
|
logger.debug('createActiveSpeakerObserver()');
|
|
717
699
|
if (typeof interval !== 'number') {
|
|
@@ -804,6 +786,24 @@ class RouterImpl extends enhancedEvents_1.EnhancedEventEmitter {
|
|
|
804
786
|
const rtpCapabilities = ortc.generateRouterRtpCapabilities(clonedMediaCodecs);
|
|
805
787
|
this.#data.rtpCapabilities = rtpCapabilities;
|
|
806
788
|
}
|
|
789
|
+
addPipeTransportPair(pipeTransportPairKey, pipeTransportPairPromise) {
|
|
790
|
+
if (this.#mapRouterPairPipeTransportPairPromise.has(pipeTransportPairKey)) {
|
|
791
|
+
throw new Error('given pipeTransportPairKey already exists in this Router');
|
|
792
|
+
}
|
|
793
|
+
this.#mapRouterPairPipeTransportPairPromise.set(pipeTransportPairKey, pipeTransportPairPromise);
|
|
794
|
+
pipeTransportPairPromise
|
|
795
|
+
.then(pipeTransportPair => {
|
|
796
|
+
const localPipeTransport = pipeTransportPair[this.id];
|
|
797
|
+
// NOTE: No need to do any other cleanup here since that is done by the
|
|
798
|
+
// Router calling this method on us.
|
|
799
|
+
localPipeTransport.observer.on('close', () => {
|
|
800
|
+
this.#mapRouterPairPipeTransportPairPromise.delete(pipeTransportPairKey);
|
|
801
|
+
});
|
|
802
|
+
})
|
|
803
|
+
.catch(() => {
|
|
804
|
+
this.#mapRouterPairPipeTransportPairPromise.delete(pipeTransportPairKey);
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
807
|
handleListenerError() {
|
|
808
808
|
this.on('listenererror', (eventName, error) => {
|
|
809
809
|
logger.error(`event listener threw an error [eventName:${eventName}]:`, error);
|
|
@@ -234,10 +234,6 @@ export interface Router<RouterAppData extends AppData = AppData> extends Enhance
|
|
|
234
234
|
* Pipes the given Producer or DataProducer into another Router in same host.
|
|
235
235
|
*/
|
|
236
236
|
pipeToRouter(options: PipeToRouterOptions): Promise<PipeToRouterResult>;
|
|
237
|
-
/**
|
|
238
|
-
* @private
|
|
239
|
-
*/
|
|
240
|
-
addPipeTransportPair(pipeTransportPairKey: string, pipeTransportPairPromise: Promise<PipeTransportPair>): void;
|
|
241
237
|
/**
|
|
242
238
|
* Create an ActiveSpeakerObserver
|
|
243
239
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouterTypes.d.ts","sourceRoot":"","sources":["../src/RouterTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EACX,SAAS,EACT,mBAAmB,EACnB,iBAAiB,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACX,eAAe,EACf,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACX,cAAc,EACd,qBAAqB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,KAAK,EACX,eAAe,EACf,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EACX,qBAAqB,EACrB,4BAA4B,EAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EACX,kBAAkB,EAClB,yBAAyB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACX,eAAe,EACf,wBAAwB,EACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,aAAa,CAAC,aAAa,SAAS,OAAO,GAAG,OAAO,IAAI;IACpE;;OAEG;IACH,WAAW,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAEzC;;OAEG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAErC;;;;OAIG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC;IAEzC;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,kBAAkB,CAAC;AAEvB,KAAK,kBAAkB,GAAG,MAAM,CAAC,sBAAsB,EAAE,oBAAoB,CAAC,CAAC;AAE/E,KAAK,sBAAsB,GAAG;IAC7B,UAAU,EAAE,mBAAmB,CAAC;CAChC,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC3B;;OAEG;IACH,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAChC;;OAEG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC;IAExB;;OAEG;IACH,YAAY,CAAC,EAAE,QAAQ,CAAC;IAExB;;OAEG;IACH,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAEhC;;OAEG;IACH,gBAAgB,CAAC,EAAE,YAAY,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACxB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;OAEG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;;OAEG;IACH,wBAAwB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC9D;;OAEG;IACH,uBAAuB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1D;;OAEG;IACH,wBAAwB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC9D;;OAEG;IACH,gCAAgC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACtE;;OAEG;IACH,+BAA+B,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAC1B,WAAW,EAAE,EAAE,CAAC;IAEhB,QAAQ,EAAE,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG;IAClC,KAAK,EAAE,EAAE,CAAC;IACV,YAAY,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1B,cAAc,EAAE,CAAC,WAAW,CAAC,CAAC;CAC9B,CAAC;AAEF,MAAM,WAAW,MAAM,CACtB,aAAa,SAAS,OAAO,GAAG,OAAO,CACtC,SAAQ,oBAAoB,CAAC,YAAY,CAAC;IAC3C;;OAEG;IACH,IAAI,EAAE,IAAI,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAAC;IAEtB;;OAEG;IACH,IAAI,eAAe,IAAI,eAAe,CAAC;IAEvC;;OAEG;IACH,IAAI,OAAO,IAAI,aAAa,CAAC;IAE7B;;OAEG;IACH,IAAI,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE;IAEpC;;OAEG;IACH,IAAI,QAAQ,IAAI,cAAc,CAAC;IAE/B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;;;OAIG;IACH,YAAY,IAAI,IAAI,CAAC;IAErB;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAE5B;;OAEG;IACH,qBAAqB,CAAC,sBAAsB,SAAS,OAAO,GAAG,OAAO,EACrE,OAAO,EAAE,sBAAsB,CAAC,sBAAsB,CAAC,GACrD,OAAO,CAAC,eAAe,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,oBAAoB,CAAC,qBAAqB,SAAS,OAAO,GAAG,OAAO,EACnE,OAAO,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,GACnD,OAAO,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAElD;;OAEG;IACH,mBAAmB,CAAC,oBAAoB,SAAS,OAAO,GAAG,OAAO,EACjE,OAAO,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,GACjD,OAAO,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAEhD;;OAEG;IACH,qBAAqB,CAAC,sBAAsB,SAAS,OAAO,GAAG,OAAO,EACrE,OAAO,CAAC,EAAE,sBAAsB,CAAC,sBAAsB,CAAC,GACtD,OAAO,CAAC,eAAe,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAExE;;OAEG;IACH,
|
|
1
|
+
{"version":3,"file":"RouterTypes.d.ts","sourceRoot":"","sources":["../src/RouterTypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -901,7 +901,7 @@ test('router.pipeToRouter() succeeds with data', async () => {
|
|
|
901
901
|
expect(pipeDataProducer.label).toBe('foo');
|
|
902
902
|
expect(pipeDataProducer.protocol).toBe('bar');
|
|
903
903
|
}, 2000);
|
|
904
|
-
test('transport.
|
|
904
|
+
test('transport.consumeData() for a pipe DataProducer succeeds', async () => {
|
|
905
905
|
const { pipeDataProducer } = await ctx.router1.pipeToRouter({
|
|
906
906
|
dataProducerId: ctx.dataProducer.id,
|
|
907
907
|
router: ctx.router2,
|
|
@@ -920,6 +920,38 @@ test('transport.dataConsume() for a pipe DataProducer succeeds', async () => {
|
|
|
920
920
|
expect(dataConsumer.label).toBe('foo');
|
|
921
921
|
expect(dataConsumer.protocol).toBe('bar');
|
|
922
922
|
}, 2000);
|
|
923
|
+
test('transport.consumeData() for a pipe DataProducer succeeds with subchannels', async () => {
|
|
924
|
+
const directTransport1 = await ctx.router1.createDirectTransport();
|
|
925
|
+
const directDataProducer = await directTransport1.produceData({
|
|
926
|
+
label: 'foo',
|
|
927
|
+
protocol: 'bar',
|
|
928
|
+
});
|
|
929
|
+
await ctx.router1.pipeToRouter({
|
|
930
|
+
dataProducerId: directDataProducer.id,
|
|
931
|
+
router: ctx.router2,
|
|
932
|
+
});
|
|
933
|
+
const directTransport2 = await ctx.router2.createDirectTransport();
|
|
934
|
+
const directDataConsumer = await directTransport2.consumeData({
|
|
935
|
+
dataProducerId: directDataProducer.id,
|
|
936
|
+
subchannels: [123, 666],
|
|
937
|
+
});
|
|
938
|
+
await new Promise((resolve, reject) => {
|
|
939
|
+
directDataConsumer.on('message', (message, ppid) => {
|
|
940
|
+
try {
|
|
941
|
+
expect(message.toString('utf8')).toBe('hello');
|
|
942
|
+
expect(ppid).toBe(51);
|
|
943
|
+
resolve();
|
|
944
|
+
}
|
|
945
|
+
catch (error) {
|
|
946
|
+
reject(error);
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
directDataProducer.send('hello',
|
|
950
|
+
/* ppid */ undefined,
|
|
951
|
+
/* subchannels */ [123, 124],
|
|
952
|
+
/* requiredSubchannel */ 666);
|
|
953
|
+
});
|
|
954
|
+
}, 2000);
|
|
923
955
|
test('dataProducer.close() is transmitted to pipe DataConsumer', async () => {
|
|
924
956
|
const { pipeDataProducer } = await ctx.router1.pipeToRouter({
|
|
925
957
|
dataProducerId: ctx.dataProducer.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mediasoup",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.22.0",
|
|
4
4
|
"description": "Cutting Edge WebRTC Video Conferencing",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
|
|
@@ -129,14 +129,14 @@
|
|
|
129
129
|
"globals": "^17.7.0",
|
|
130
130
|
"ini": "^7.0.0",
|
|
131
131
|
"jest": "^30.4.2",
|
|
132
|
-
"knip": "^6.
|
|
132
|
+
"knip": "^6.27.0",
|
|
133
133
|
"marked": "^18.0.6",
|
|
134
134
|
"open-cli": "^9.0.0",
|
|
135
135
|
"pick-port": "^2.2.1",
|
|
136
136
|
"prettier": "^3.9.5",
|
|
137
137
|
"ts-jest": "^29.4.11",
|
|
138
138
|
"typescript": "^6.0.3",
|
|
139
|
-
"typescript-eslint": "^8.
|
|
139
|
+
"typescript-eslint": "^8.64.0",
|
|
140
140
|
"werift-sctp": "^0.0.11"
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -49,7 +49,8 @@ namespace RTC
|
|
|
49
49
|
const std::string& dataProducerId,
|
|
50
50
|
RTC::DataConsumer::Listener* listener,
|
|
51
51
|
const FBS::Transport::ConsumeDataRequest* data,
|
|
52
|
-
size_t maxMessageSize
|
|
52
|
+
size_t maxMessageSize,
|
|
53
|
+
bool pipe);
|
|
53
54
|
~DataConsumer() override;
|
|
54
55
|
|
|
55
56
|
public:
|
|
@@ -67,20 +68,17 @@ namespace RTC
|
|
|
67
68
|
}
|
|
68
69
|
bool IsActive() const
|
|
69
70
|
{
|
|
70
|
-
// It's active it DataConsumer and DataProducer are not paused and the
|
|
71
|
-
// is
|
|
71
|
+
// It's active it DataConsumer and DataProducer are not paused and the SCTP
|
|
72
|
+
// association (if any) is not closed.
|
|
72
73
|
// clang-format off
|
|
73
74
|
return (
|
|
74
|
-
this->
|
|
75
|
-
(this->type == DataConsumer::Type::DIRECT || this->sctpAssociationConnected) &&
|
|
75
|
+
(this->type == DataConsumer::Type::DIRECT || !this->sctpAssociationClosed) &&
|
|
76
76
|
!this->paused &&
|
|
77
77
|
!this->dataProducerPaused &&
|
|
78
78
|
!this->dataProducerClosed
|
|
79
79
|
);
|
|
80
80
|
// clang-format on
|
|
81
81
|
}
|
|
82
|
-
void TransportConnected();
|
|
83
|
-
void TransportDisconnected();
|
|
84
82
|
bool IsPaused() const
|
|
85
83
|
{
|
|
86
84
|
return this->paused;
|
|
@@ -91,7 +89,6 @@ namespace RTC
|
|
|
91
89
|
}
|
|
92
90
|
void DataProducerPaused();
|
|
93
91
|
void DataProducerResumed();
|
|
94
|
-
void SctpAssociationConnected();
|
|
95
92
|
void SctpAssociationClosed();
|
|
96
93
|
void SctpBufferedAmountLow(uint32_t bufferedAmount) const;
|
|
97
94
|
void SctpSendBufferFull() const;
|
|
@@ -116,14 +113,14 @@ namespace RTC
|
|
|
116
113
|
SharedInterface* shared{ nullptr };
|
|
117
114
|
RTC::DataConsumer::Listener* listener{ nullptr };
|
|
118
115
|
size_t maxMessageSize{ 0u };
|
|
116
|
+
bool pipe{ false };
|
|
119
117
|
// Others.
|
|
120
118
|
Type type;
|
|
121
119
|
RTC::SctpStreamParameters sctpStreamParameters;
|
|
122
120
|
std::string label;
|
|
123
121
|
std::string protocol;
|
|
124
122
|
ankerl::unordered_dense::set<uint16_t> subchannels;
|
|
125
|
-
bool
|
|
126
|
-
bool sctpAssociationConnected{ false };
|
|
123
|
+
bool sctpAssociationClosed{ false };
|
|
127
124
|
bool paused{ false };
|
|
128
125
|
bool dataProducerPaused{ false };
|
|
129
126
|
bool dataProducerClosed{ false };
|
|
@@ -68,6 +68,22 @@ namespace RTC
|
|
|
68
68
|
return this->payload.size();
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Replace the whole payload of the message.
|
|
73
|
+
*/
|
|
74
|
+
void SetPayload(std::vector<uint8_t> payload)
|
|
75
|
+
{
|
|
76
|
+
this->payload = std::move(payload);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Remove the first `len` bytes from the beginning of the payload.
|
|
81
|
+
*/
|
|
82
|
+
void RemovePayloadFront(size_t len)
|
|
83
|
+
{
|
|
84
|
+
this->payload.erase(this->payload.begin(), this->payload.begin() + len);
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
/**
|
|
72
88
|
* Useful to extract the payload and its ownership when destructing the
|
|
73
89
|
* message.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#ifndef MS_RTC_SUBCHANNELS_CODEC_HPP
|
|
2
|
+
#define MS_RTC_SUBCHANNELS_CODEC_HPP
|
|
3
|
+
|
|
4
|
+
#include "common.hpp"
|
|
5
|
+
#include "RTC/SCTP/public/Message.hpp"
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace RTC
|
|
9
|
+
{
|
|
10
|
+
/**
|
|
11
|
+
* Helper to (optionally) encode/decode subchannels and required subchannel at
|
|
12
|
+
* the beginning of an SCTP message payload so that this information travels
|
|
13
|
+
* within the SCTP message itself.
|
|
14
|
+
*/
|
|
15
|
+
class SubchannelsCodec
|
|
16
|
+
{
|
|
17
|
+
/**
|
|
18
|
+
* Wire layout of the encoded header prepended to the message payload (all
|
|
19
|
+
* fields in network byte order). `R` is the `requiredSubchannelFlag`, i.e.
|
|
20
|
+
* the least significant bit of the Magic Token. The `requiredSubchannel`
|
|
21
|
+
* field is only present when `R` is 1.
|
|
22
|
+
*
|
|
23
|
+
* 0 1 2 3
|
|
24
|
+
* 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
|
|
25
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
26
|
+
* | Magic Token (1/2) |
|
|
27
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
28
|
+
* | Magic Token (2/2) |R|
|
|
29
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
30
|
+
* | subchannelsCount | Subchannel 0 |
|
|
31
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
32
|
+
* | ... |
|
|
33
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
34
|
+
* | Subchannel N-1 | requiredSubchannel (if R=1) |
|
|
35
|
+
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
public:
|
|
39
|
+
/**
|
|
40
|
+
* 8-byte Magic Token that prefixes an encoded payload. It is intentionally
|
|
41
|
+
* made of non-printable high bytes and starts with a byte that is not a valid
|
|
42
|
+
* UTF-8 leading byte (0x80-0xBF), so a real string message (valid UTF-8) can
|
|
43
|
+
* never begin with it and the chance of colliding with a binary message is
|
|
44
|
+
* negligible. Its least significant bit is reserved as the
|
|
45
|
+
* `requiredSubchannelFlag`, so it is always 0 in the token constant itself.
|
|
46
|
+
*/
|
|
47
|
+
static constexpr uint64_t MagicToken{ 0xA5F09CB3D6E1F28C };
|
|
48
|
+
static constexpr uint64_t RequiredSubchannelFlagMask{ 0x1 };
|
|
49
|
+
|
|
50
|
+
public:
|
|
51
|
+
/**
|
|
52
|
+
* If `subchannels` is not empty or `requiredSubchannel` has value, encodes
|
|
53
|
+
* them at the beginning of the given message payload.
|
|
54
|
+
*
|
|
55
|
+
* @returns true if the message was encoded, false otherwise.
|
|
56
|
+
*/
|
|
57
|
+
static bool EncodeSubchannels(
|
|
58
|
+
RTC::SCTP::Message& message,
|
|
59
|
+
const std::vector<uint16_t>& subchannels,
|
|
60
|
+
std::optional<uint16_t> requiredSubchannel);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* If the given message payload starts with the Magic Token, extracts the
|
|
64
|
+
* encoded subchannels and (optionally) the required subchannel, fills the
|
|
65
|
+
* given arguments and removes the encoded header from the message payload.
|
|
66
|
+
*
|
|
67
|
+
* @returns true if the message was successfully decoded, false otherwise.
|
|
68
|
+
*/
|
|
69
|
+
static bool DecodeSubchannels(
|
|
70
|
+
RTC::SCTP::Message& message,
|
|
71
|
+
std::vector<uint16_t>& subchannels,
|
|
72
|
+
std::optional<uint16_t>& requiredSubchannel);
|
|
73
|
+
};
|
|
74
|
+
} // namespace RTC
|
|
75
|
+
|
|
76
|
+
#endif
|
|
@@ -210,7 +210,7 @@ namespace RTC
|
|
|
210
210
|
const std::string& dataProducerId, const std::string& method) const final;
|
|
211
211
|
virtual RTC::DataConsumer* AssertAndGetDataConsumerById(
|
|
212
212
|
const std::string& dataConsumerId, const std::string& method) const final;
|
|
213
|
-
virtual RTC::DataConsumer*
|
|
213
|
+
virtual RTC::DataConsumer* GetSctpDataConsumerByStreamId(uint16_t streamId) const final;
|
|
214
214
|
virtual void CheckNoProducer(const std::string& producerId, const std::string& method) const final;
|
|
215
215
|
virtual void CheckNoConsumer(const std::string& consumerId, const std::string& method) const final;
|
|
216
216
|
virtual void CheckNoDataProducer(
|
|
@@ -219,6 +219,10 @@ namespace RTC
|
|
|
219
219
|
const std::string& dataConsumerId, const std::string& method) const final;
|
|
220
220
|
virtual void CheckNoSctpDataConsumer(uint16_t streamId, const std::string& method) const final;
|
|
221
221
|
virtual bool IsConnected() const = 0;
|
|
222
|
+
virtual bool IsPipe() const
|
|
223
|
+
{
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
222
226
|
virtual void SendRtpPacket(
|
|
223
227
|
RTC::Consumer* consumer, RTC::RTP::Packet* packet, const onSendCallback* cb = nullptr) = 0;
|
|
224
228
|
virtual void HandleRtcpPacket(RTC::RTCP::Packet* packet) final;
|
package/worker/meson.build
CHANGED
|
@@ -136,6 +136,7 @@ common_sources = [
|
|
|
136
136
|
'src/RTC/SimpleProducerStreamManager.cpp',
|
|
137
137
|
'src/RTC/SimulcastProducerStreamManager.cpp',
|
|
138
138
|
'src/RTC/SrtpSession.cpp',
|
|
139
|
+
'src/RTC/SubchannelsCodec.cpp',
|
|
139
140
|
'src/RTC/SvcProducerStreamManager.cpp',
|
|
140
141
|
'src/RTC/TcpConnection.cpp',
|
|
141
142
|
'src/RTC/TcpServer.cpp',
|
|
@@ -430,6 +431,7 @@ test_sources = [
|
|
|
430
431
|
'test/src/RTC/TestRateCalculator.cpp',
|
|
431
432
|
'test/src/RTC/TestRtpEncodingParameters.cpp',
|
|
432
433
|
'test/src/RTC/TestSeqManager.cpp',
|
|
434
|
+
'test/src/RTC/TestSubchannelsCodec.cpp',
|
|
433
435
|
'test/src/RTC/TestTransportCongestionControlServer.cpp',
|
|
434
436
|
'test/src/RTC/TestTransportTuple.cpp',
|
|
435
437
|
'test/src/RTC/TestTrendCalculator.cpp',
|
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"node_modules/brace-expansion": {
|
|
24
|
-
"version": "5.0.
|
|
25
|
-
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.
|
|
26
|
-
"integrity": "sha512-
|
|
24
|
+
"version": "5.0.7",
|
|
25
|
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
|
26
|
+
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
|
27
|
+
"license": "MIT",
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"balanced-match": "^4.0.2"
|
|
29
30
|
},
|
|
@@ -104,9 +105,9 @@
|
|
|
104
105
|
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="
|
|
105
106
|
},
|
|
106
107
|
"brace-expansion": {
|
|
107
|
-
"version": "5.0.
|
|
108
|
-
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.
|
|
109
|
-
"integrity": "sha512-
|
|
108
|
+
"version": "5.0.7",
|
|
109
|
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
|
110
|
+
"integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
|
|
110
111
|
"requires": {
|
|
111
112
|
"balanced-match": "^4.0.2"
|
|
112
113
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#include "RTC/DataConsumer.hpp"
|
|
5
5
|
#include "Logger.hpp"
|
|
6
6
|
#include "MediaSoupErrors.hpp"
|
|
7
|
+
#include "RTC/SubchannelsCodec.hpp"
|
|
7
8
|
|
|
8
9
|
namespace RTC
|
|
9
10
|
{
|
|
@@ -15,12 +16,14 @@ namespace RTC
|
|
|
15
16
|
const std::string& dataProducerId,
|
|
16
17
|
RTC::DataConsumer::Listener* listener,
|
|
17
18
|
const FBS::Transport::ConsumeDataRequest* data,
|
|
18
|
-
size_t maxMessageSize
|
|
19
|
+
size_t maxMessageSize,
|
|
20
|
+
bool pipe)
|
|
19
21
|
: id(id),
|
|
20
22
|
dataProducerId(dataProducerId),
|
|
21
23
|
shared(shared),
|
|
22
24
|
listener(listener),
|
|
23
|
-
maxMessageSize(maxMessageSize)
|
|
25
|
+
maxMessageSize(maxMessageSize),
|
|
26
|
+
pipe(pipe)
|
|
24
27
|
{
|
|
25
28
|
MS_TRACE();
|
|
26
29
|
|
|
@@ -398,24 +401,6 @@ namespace RTC
|
|
|
398
401
|
}
|
|
399
402
|
}
|
|
400
403
|
|
|
401
|
-
void DataConsumer::TransportConnected()
|
|
402
|
-
{
|
|
403
|
-
MS_TRACE();
|
|
404
|
-
|
|
405
|
-
this->transportConnected = true;
|
|
406
|
-
|
|
407
|
-
MS_DEBUG_DEV("Transport connected [dataConsumerId:%s]", this->id.c_str());
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
void DataConsumer::TransportDisconnected()
|
|
411
|
-
{
|
|
412
|
-
MS_TRACE();
|
|
413
|
-
|
|
414
|
-
this->transportConnected = false;
|
|
415
|
-
|
|
416
|
-
MS_DEBUG_DEV("Transport disconnected [dataConsumerId:%s]", this->id.c_str());
|
|
417
|
-
}
|
|
418
|
-
|
|
419
404
|
void DataConsumer::DataProducerPaused()
|
|
420
405
|
{
|
|
421
406
|
MS_TRACE();
|
|
@@ -450,22 +435,11 @@ namespace RTC
|
|
|
450
435
|
this->id, FBS::Notification::Event::DATACONSUMER_DATAPRODUCER_RESUME);
|
|
451
436
|
}
|
|
452
437
|
|
|
453
|
-
void DataConsumer::SctpAssociationConnected()
|
|
454
|
-
{
|
|
455
|
-
MS_TRACE();
|
|
456
|
-
|
|
457
|
-
this->sctpAssociationConnected = true;
|
|
458
|
-
|
|
459
|
-
MS_DEBUG_DEV("SctpAssociation connected [dataConsumerId:%s]", this->id.c_str());
|
|
460
|
-
}
|
|
461
|
-
|
|
462
438
|
void DataConsumer::SctpAssociationClosed()
|
|
463
439
|
{
|
|
464
440
|
MS_TRACE();
|
|
465
441
|
|
|
466
|
-
this->
|
|
467
|
-
|
|
468
|
-
MS_DEBUG_DEV("SctpAssociation closed [dataConsumerId:%s]", this->id.c_str());
|
|
442
|
+
this->sctpAssociationClosed = true;
|
|
469
443
|
}
|
|
470
444
|
|
|
471
445
|
void DataConsumer::SctpBufferedAmountLow(uint32_t bufferedAmount) const
|
|
@@ -526,48 +500,57 @@ namespace RTC
|
|
|
526
500
|
return false;
|
|
527
501
|
}
|
|
528
502
|
|
|
529
|
-
|
|
530
|
-
// subscribed to it.
|
|
531
|
-
if (
|
|
532
|
-
requiredSubchannel.has_value() &&
|
|
533
|
-
this->subchannels.find(requiredSubchannel.value()) == this->subchannels.end())
|
|
503
|
+
if (!this->pipe)
|
|
534
504
|
{
|
|
535
|
-
|
|
505
|
+
// If a required subchannel is given, verify that this data consumer is
|
|
506
|
+
// subscribed to it.
|
|
507
|
+
if (requiredSubchannel.has_value() && !this->subchannels.contains(requiredSubchannel.value()))
|
|
536
508
|
{
|
|
537
|
-
(
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
509
|
+
if (cb)
|
|
510
|
+
{
|
|
511
|
+
(*cb)(false, false);
|
|
512
|
+
delete cb;
|
|
513
|
+
}
|
|
543
514
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if (!subchannels.empty())
|
|
547
|
-
{
|
|
548
|
-
bool subchannelMatched{ false };
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
549
517
|
|
|
550
|
-
|
|
518
|
+
// If subchannels are given, verify that this data consumer is subscribed
|
|
519
|
+
// to at least one of them.
|
|
520
|
+
if (!subchannels.empty())
|
|
551
521
|
{
|
|
552
|
-
|
|
522
|
+
bool subchannelMatched{ false };
|
|
523
|
+
|
|
524
|
+
for (const auto subchannel : subchannels)
|
|
553
525
|
{
|
|
554
|
-
|
|
526
|
+
if (this->subchannels.contains(subchannel))
|
|
527
|
+
{
|
|
528
|
+
subchannelMatched = true;
|
|
555
529
|
|
|
556
|
-
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
557
532
|
}
|
|
558
|
-
}
|
|
559
533
|
|
|
560
|
-
|
|
561
|
-
{
|
|
562
|
-
if (cb)
|
|
534
|
+
if (!subchannelMatched)
|
|
563
535
|
{
|
|
564
|
-
(
|
|
565
|
-
|
|
566
|
-
|
|
536
|
+
if (cb)
|
|
537
|
+
{
|
|
538
|
+
(*cb)(false, false);
|
|
539
|
+
delete cb;
|
|
540
|
+
}
|
|
567
541
|
|
|
568
|
-
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
569
544
|
}
|
|
570
545
|
}
|
|
546
|
+
// This is a piped DataConsumer, so instead of verifying subchannels locally,
|
|
547
|
+
// encode the subchannels and required subchannel at the beginning of the
|
|
548
|
+
// message payload so the receiving PipeTransport can decode them and apply
|
|
549
|
+
// them to its own DataConsumers.
|
|
550
|
+
else
|
|
551
|
+
{
|
|
552
|
+
RTC::SubchannelsCodec::EncodeSubchannels(message, subchannels, requiredSubchannel);
|
|
553
|
+
}
|
|
571
554
|
|
|
572
555
|
const size_t messageLen = message.GetPayloadLength();
|
|
573
556
|
|
|
@@ -575,7 +558,7 @@ namespace RTC
|
|
|
575
558
|
{
|
|
576
559
|
MS_WARN_TAG(
|
|
577
560
|
message,
|
|
578
|
-
"
|
|
561
|
+
"message exceeds maxMessageSize value [maxMessageSize:%zu, len:%zu]",
|
|
579
562
|
messageLen,
|
|
580
563
|
this->maxMessageSize);
|
|
581
564
|
|
|
@@ -446,7 +446,7 @@ namespace RTC
|
|
|
446
446
|
{
|
|
447
447
|
MS_TRACE();
|
|
448
448
|
|
|
449
|
-
auto it = this->mapTransports.find(transportId);
|
|
449
|
+
const auto it = this->mapTransports.find(transportId);
|
|
450
450
|
|
|
451
451
|
if (this->mapTransports.find(transportId) == this->mapTransports.end())
|
|
452
452
|
{
|
|
@@ -461,7 +461,7 @@ namespace RTC
|
|
|
461
461
|
{
|
|
462
462
|
MS_TRACE();
|
|
463
463
|
|
|
464
|
-
auto it = this->mapRtpObservers.find(rtpObserverId);
|
|
464
|
+
const auto it = this->mapRtpObservers.find(rtpObserverId);
|
|
465
465
|
|
|
466
466
|
if (this->mapRtpObservers.find(rtpObserverId) == this->mapRtpObservers.end())
|
|
467
467
|
{
|
|
@@ -566,7 +566,7 @@ namespace RTC
|
|
|
566
566
|
consumer->ProducerPaused();
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
-
auto it = this->mapProducerRtpObservers.find(producer);
|
|
569
|
+
const auto it = this->mapProducerRtpObservers.find(producer);
|
|
570
570
|
|
|
571
571
|
if (it != this->mapProducerRtpObservers.end())
|
|
572
572
|
{
|
|
@@ -590,7 +590,7 @@ namespace RTC
|
|
|
590
590
|
consumer->ProducerResumed();
|
|
591
591
|
}
|
|
592
592
|
|
|
593
|
-
auto it = this->mapProducerRtpObservers.find(producer);
|
|
593
|
+
const auto it = this->mapProducerRtpObservers.find(producer);
|
|
594
594
|
|
|
595
595
|
if (it != this->mapProducerRtpObservers.end())
|
|
596
596
|
{
|
|
@@ -688,7 +688,7 @@ namespace RTC
|
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
690
|
|
|
691
|
-
auto it = this->mapProducerRtpObservers.find(producer);
|
|
691
|
+
const auto it = this->mapProducerRtpObservers.find(producer);
|
|
692
692
|
|
|
693
693
|
if (it != this->mapProducerRtpObservers.end())
|
|
694
694
|
{
|
|
@@ -1076,7 +1076,7 @@ namespace RTC
|
|
|
1076
1076
|
RTC::Producer* Router::RtpObserverGetProducer(
|
|
1077
1077
|
RTC::RtpObserver* /* rtpObserver */, const std::string& id)
|
|
1078
1078
|
{
|
|
1079
|
-
auto it = this->mapProducers.find(id);
|
|
1079
|
+
const auto it = this->mapProducers.find(id);
|
|
1080
1080
|
|
|
1081
1081
|
if (it == this->mapProducers.end())
|
|
1082
1082
|
{
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::SubchannelsCodec"
|
|
2
|
+
// #define MS_LOG_DEV_LEVEL 3
|
|
3
|
+
|
|
4
|
+
#include "RTC/SubchannelsCodec.hpp"
|
|
5
|
+
#include "Logger.hpp"
|
|
6
|
+
#include "Utils.hpp"
|
|
7
|
+
|
|
8
|
+
namespace RTC
|
|
9
|
+
{
|
|
10
|
+
/* Class methods. */
|
|
11
|
+
|
|
12
|
+
bool SubchannelsCodec::EncodeSubchannels(
|
|
13
|
+
RTC::SCTP::Message& message,
|
|
14
|
+
const std::vector<uint16_t>& subchannels,
|
|
15
|
+
std::optional<uint16_t> requiredSubchannel)
|
|
16
|
+
{
|
|
17
|
+
MS_TRACE();
|
|
18
|
+
|
|
19
|
+
// Nothing to encode.
|
|
20
|
+
if (subchannels.empty() && !requiredSubchannel.has_value())
|
|
21
|
+
{
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (subchannels.size() > 0xFFFF)
|
|
26
|
+
{
|
|
27
|
+
MS_ERROR("too many subchannels to encode [count:%zu]", subchannels.size());
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const auto subchannelsCount = static_cast<uint16_t>(subchannels.size());
|
|
33
|
+
const bool requiredSubchannelFlag = requiredSubchannel.has_value();
|
|
34
|
+
|
|
35
|
+
// Length of the header to prepend.
|
|
36
|
+
size_t headerLen =
|
|
37
|
+
8 /* Magic Token */ + 2 /* subchannelsCount */ + (static_cast<size_t>(subchannelsCount) * 2);
|
|
38
|
+
|
|
39
|
+
if (requiredSubchannelFlag)
|
|
40
|
+
{
|
|
41
|
+
headerLen += 2;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const auto payload = message.GetPayload();
|
|
45
|
+
|
|
46
|
+
// Build the whole new payload at once (header followed by the original
|
|
47
|
+
// payload) so it can be moved into the message with a single allocation.
|
|
48
|
+
std::vector<uint8_t> newPayload(headerLen + payload.size());
|
|
49
|
+
|
|
50
|
+
uint64_t magicToken = SubchannelsCodec::MagicToken;
|
|
51
|
+
|
|
52
|
+
if (requiredSubchannelFlag)
|
|
53
|
+
{
|
|
54
|
+
magicToken |= SubchannelsCodec::RequiredSubchannelFlagMask;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
size_t offset = 0;
|
|
58
|
+
|
|
59
|
+
Utils::Byte::Set8Bytes(newPayload.data(), offset, magicToken);
|
|
60
|
+
offset += 8;
|
|
61
|
+
|
|
62
|
+
Utils::Byte::Set2Bytes(newPayload.data(), offset, subchannelsCount);
|
|
63
|
+
offset += 2;
|
|
64
|
+
|
|
65
|
+
for (const auto subchannel : subchannels)
|
|
66
|
+
{
|
|
67
|
+
Utils::Byte::Set2Bytes(newPayload.data(), offset, subchannel);
|
|
68
|
+
offset += 2;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (requiredSubchannelFlag)
|
|
72
|
+
{
|
|
73
|
+
Utils::Byte::Set2Bytes(newPayload.data(), offset, requiredSubchannel.value());
|
|
74
|
+
offset += 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Append the original payload right after the encoded header.
|
|
78
|
+
std::ranges::copy(payload, newPayload.begin() + offset);
|
|
79
|
+
|
|
80
|
+
message.SetPayload(std::move(newPayload));
|
|
81
|
+
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
bool SubchannelsCodec::DecodeSubchannels(
|
|
86
|
+
RTC::SCTP::Message& message,
|
|
87
|
+
std::vector<uint16_t>& subchannels,
|
|
88
|
+
std::optional<uint16_t>& requiredSubchannel)
|
|
89
|
+
{
|
|
90
|
+
MS_TRACE();
|
|
91
|
+
|
|
92
|
+
const auto payload = message.GetPayload();
|
|
93
|
+
const size_t len = payload.size();
|
|
94
|
+
|
|
95
|
+
// Not even enough bytes to hold the Magic Token and `subchannelsCount` field,
|
|
96
|
+
// so nothing to decode.
|
|
97
|
+
if (len < 10)
|
|
98
|
+
{
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const uint64_t magicToken = Utils::Byte::Get8Bytes(payload.data(), 0);
|
|
103
|
+
|
|
104
|
+
// The message does not start with the Magic Token, so nothing to decode.
|
|
105
|
+
if ((magicToken & ~SubchannelsCodec::RequiredSubchannelFlagMask) != SubchannelsCodec::MagicToken)
|
|
106
|
+
{
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const bool requiredSubchannelFlag =
|
|
111
|
+
(magicToken & SubchannelsCodec::RequiredSubchannelFlagMask) != 0;
|
|
112
|
+
|
|
113
|
+
const uint16_t subchannelsCount = Utils::Byte::Get2Bytes(payload.data(), 8);
|
|
114
|
+
|
|
115
|
+
// Offset right after the Magic Token and subchannelsCount fields.
|
|
116
|
+
size_t offset = 10;
|
|
117
|
+
|
|
118
|
+
// Bytes needed to hold all announced subchannels and, if present, the
|
|
119
|
+
// requiredSubchannel.
|
|
120
|
+
size_t neededLen = offset + (static_cast<size_t>(subchannelsCount) * 2);
|
|
121
|
+
|
|
122
|
+
if (requiredSubchannelFlag)
|
|
123
|
+
{
|
|
124
|
+
neededLen += 2;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (len < neededLen)
|
|
128
|
+
{
|
|
129
|
+
MS_WARN_DEV("message too short to hold announced subchannels, ignoring");
|
|
130
|
+
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
subchannels.reserve(subchannelsCount);
|
|
135
|
+
|
|
136
|
+
for (uint16_t i = 0; i < subchannelsCount; ++i)
|
|
137
|
+
{
|
|
138
|
+
subchannels.push_back(Utils::Byte::Get2Bytes(payload.data(), offset));
|
|
139
|
+
offset += 2;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (requiredSubchannelFlag)
|
|
143
|
+
{
|
|
144
|
+
requiredSubchannel = Utils::Byte::Get2Bytes(payload.data(), offset);
|
|
145
|
+
offset += 2;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Remove the decoded header from the beginning of the message payload.
|
|
149
|
+
message.RemovePayloadFront(offset);
|
|
150
|
+
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
} // namespace RTC
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#include "RTC/RtpDictionaries.hpp"
|
|
19
19
|
#include "RTC/SCTP/association/Association.hpp"
|
|
20
20
|
#include "RTC/SCTP/public/SctpOptions.hpp"
|
|
21
|
+
#include "RTC/SubchannelsCodec.hpp"
|
|
21
22
|
#include "Utils.hpp"
|
|
22
23
|
#ifdef MS_RTC_LOGGER_RTP
|
|
23
24
|
#include "RTC/RtcLogger.hpp"
|
|
@@ -1111,7 +1112,13 @@ namespace RTC
|
|
|
1111
1112
|
|
|
1112
1113
|
// This may throw.
|
|
1113
1114
|
auto* dataConsumer = new RTC::DataConsumer(
|
|
1114
|
-
this->shared,
|
|
1115
|
+
this->shared,
|
|
1116
|
+
dataConsumerId,
|
|
1117
|
+
dataProducerId,
|
|
1118
|
+
this,
|
|
1119
|
+
body,
|
|
1120
|
+
this->maxSendMessageSize,
|
|
1121
|
+
this->IsPipe());
|
|
1115
1122
|
|
|
1116
1123
|
// Verify the type of the DataConsumer.
|
|
1117
1124
|
switch (dataConsumer->GetType())
|
|
@@ -1189,19 +1196,8 @@ namespace RTC
|
|
|
1189
1196
|
|
|
1190
1197
|
request->Accept(FBS::Response::Body::DataConsumer_DumpResponse, dumpOffset);
|
|
1191
1198
|
|
|
1192
|
-
if (IsConnected())
|
|
1193
|
-
{
|
|
1194
|
-
dataConsumer->TransportConnected();
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
1199
|
if (dataConsumer->GetType() == RTC::DataConsumer::Type::SCTP)
|
|
1198
1200
|
{
|
|
1199
|
-
if (this->sctpAssociation->GetAssociationState() == RTC::SCTP::Types::AssociationState::CONNECTED)
|
|
1200
|
-
{
|
|
1201
|
-
// Tell to the DataConsumer.
|
|
1202
|
-
dataConsumer->SctpAssociationConnected();
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
1201
|
// Tell to the SCTP association.
|
|
1206
1202
|
this->sctpAssociation->MayConnect();
|
|
1207
1203
|
}
|
|
@@ -1401,6 +1397,10 @@ namespace RTC
|
|
|
1401
1397
|
|
|
1402
1398
|
if (this->sctpAssociation)
|
|
1403
1399
|
{
|
|
1400
|
+
// NOTE: This must be called after removing data consumers from the maps,
|
|
1401
|
+
// otherwise if `OnAssociationStreamBufferedAmountLow()` was triggered it
|
|
1402
|
+
// would end up emitting an event associated to an already closed data
|
|
1403
|
+
// consumer.
|
|
1404
1404
|
this->sctpAssociation->ResetStreams(
|
|
1405
1405
|
std::array<uint16_t, 1>{ dataConsumer->GetSctpStreamParameters().streamId });
|
|
1406
1406
|
}
|
|
@@ -1476,14 +1476,6 @@ namespace RTC
|
|
|
1476
1476
|
consumer->TransportConnected();
|
|
1477
1477
|
}
|
|
1478
1478
|
|
|
1479
|
-
// Tell all DataConsumers.
|
|
1480
|
-
for (auto& kv : this->mapDataConsumers)
|
|
1481
|
-
{
|
|
1482
|
-
auto* dataConsumer = kv.second;
|
|
1483
|
-
|
|
1484
|
-
dataConsumer->TransportConnected();
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
1479
|
// Tell the SctpAssociation.
|
|
1488
1480
|
if (this->sctpAssociation)
|
|
1489
1481
|
{
|
|
@@ -1526,14 +1518,6 @@ namespace RTC
|
|
|
1526
1518
|
consumer->TransportDisconnected();
|
|
1527
1519
|
}
|
|
1528
1520
|
|
|
1529
|
-
// Tell all DataConsumers.
|
|
1530
|
-
for (auto& kv : this->mapDataConsumers)
|
|
1531
|
-
{
|
|
1532
|
-
auto* dataConsumer = kv.second;
|
|
1533
|
-
|
|
1534
|
-
dataConsumer->TransportDisconnected();
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
1521
|
// Stop the RTCP timer.
|
|
1538
1522
|
this->rtcpTimer->Stop();
|
|
1539
1523
|
|
|
@@ -1764,7 +1748,7 @@ namespace RTC
|
|
|
1764
1748
|
{
|
|
1765
1749
|
MS_TRACE();
|
|
1766
1750
|
|
|
1767
|
-
auto it = this->mapProducers.find(producerId);
|
|
1751
|
+
const auto it = this->mapProducers.find(producerId);
|
|
1768
1752
|
|
|
1769
1753
|
if (it == this->mapProducers.end())
|
|
1770
1754
|
{
|
|
@@ -1779,7 +1763,7 @@ namespace RTC
|
|
|
1779
1763
|
{
|
|
1780
1764
|
MS_TRACE();
|
|
1781
1765
|
|
|
1782
|
-
auto it = this->mapConsumers.find(consumerId);
|
|
1766
|
+
const auto it = this->mapConsumers.find(consumerId);
|
|
1783
1767
|
|
|
1784
1768
|
if (it == this->mapConsumers.end())
|
|
1785
1769
|
{
|
|
@@ -1793,7 +1777,7 @@ namespace RTC
|
|
|
1793
1777
|
{
|
|
1794
1778
|
MS_TRACE();
|
|
1795
1779
|
|
|
1796
|
-
auto mapSsrcConsumerIt = this->mapSsrcConsumer.find(ssrc);
|
|
1780
|
+
const auto mapSsrcConsumerIt = this->mapSsrcConsumer.find(ssrc);
|
|
1797
1781
|
|
|
1798
1782
|
if (mapSsrcConsumerIt == this->mapSsrcConsumer.end())
|
|
1799
1783
|
{
|
|
@@ -1809,7 +1793,7 @@ namespace RTC
|
|
|
1809
1793
|
{
|
|
1810
1794
|
MS_TRACE();
|
|
1811
1795
|
|
|
1812
|
-
auto mapRtxSsrcConsumerIt = this->mapRtxSsrcConsumer.find(ssrc);
|
|
1796
|
+
const auto mapRtxSsrcConsumerIt = this->mapRtxSsrcConsumer.find(ssrc);
|
|
1813
1797
|
|
|
1814
1798
|
if (mapRtxSsrcConsumerIt == this->mapRtxSsrcConsumer.end())
|
|
1815
1799
|
{
|
|
@@ -1826,7 +1810,7 @@ namespace RTC
|
|
|
1826
1810
|
{
|
|
1827
1811
|
MS_TRACE();
|
|
1828
1812
|
|
|
1829
|
-
auto it = this->mapDataProducers.find(dataProducerId);
|
|
1813
|
+
const auto it = this->mapDataProducers.find(dataProducerId);
|
|
1830
1814
|
|
|
1831
1815
|
if (it == this->mapDataProducers.end())
|
|
1832
1816
|
{
|
|
@@ -1841,7 +1825,7 @@ namespace RTC
|
|
|
1841
1825
|
{
|
|
1842
1826
|
MS_TRACE();
|
|
1843
1827
|
|
|
1844
|
-
auto it = this->mapDataConsumers.find(dataConsumerId);
|
|
1828
|
+
const auto it = this->mapDataConsumers.find(dataConsumerId);
|
|
1845
1829
|
|
|
1846
1830
|
if (it == this->mapDataConsumers.end())
|
|
1847
1831
|
{
|
|
@@ -1851,15 +1835,15 @@ namespace RTC
|
|
|
1851
1835
|
return it->second;
|
|
1852
1836
|
}
|
|
1853
1837
|
|
|
1854
|
-
RTC::DataConsumer* Transport::
|
|
1838
|
+
RTC::DataConsumer* Transport::GetSctpDataConsumerByStreamId(uint16_t streamId) const
|
|
1855
1839
|
{
|
|
1856
1840
|
MS_TRACE();
|
|
1857
1841
|
|
|
1858
|
-
auto it = this->mapSctpStreamIdDataConsumers.find(streamId);
|
|
1842
|
+
const auto it = this->mapSctpStreamIdDataConsumers.find(streamId);
|
|
1859
1843
|
|
|
1860
1844
|
if (it == this->mapSctpStreamIdDataConsumers.end())
|
|
1861
1845
|
{
|
|
1862
|
-
|
|
1846
|
+
return nullptr;
|
|
1863
1847
|
}
|
|
1864
1848
|
|
|
1865
1849
|
return it->second;
|
|
@@ -2938,6 +2922,10 @@ namespace RTC
|
|
|
2938
2922
|
this->mapSctpStreamIdDataConsumers.erase(dataConsumer->GetSctpStreamParameters().streamId);
|
|
2939
2923
|
}
|
|
2940
2924
|
|
|
2925
|
+
// NOTE: This must be called after removing the data consumer from the maps,
|
|
2926
|
+
// otherwise if `OnAssociationStreamBufferedAmountLow()` was triggered it
|
|
2927
|
+
// would end up emitting an event associated to an already closed data
|
|
2928
|
+
// consumer.
|
|
2941
2929
|
if (this->sctpAssociation)
|
|
2942
2930
|
{
|
|
2943
2931
|
this->sctpAssociation->ResetStreams(
|
|
@@ -2991,17 +2979,6 @@ namespace RTC
|
|
|
2991
2979
|
{
|
|
2992
2980
|
MS_TRACE();
|
|
2993
2981
|
|
|
2994
|
-
// Tell all DataConsumers.
|
|
2995
|
-
for (auto& kv : this->mapDataConsumers)
|
|
2996
|
-
{
|
|
2997
|
-
auto* dataConsumer = kv.second;
|
|
2998
|
-
|
|
2999
|
-
if (dataConsumer->GetType() == RTC::DataConsumer::Type::SCTP)
|
|
3000
|
-
{
|
|
3001
|
-
dataConsumer->SctpAssociationConnected();
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
|
|
3005
2982
|
// Notify the upper layer.
|
|
3006
2983
|
|
|
3007
2984
|
// First tell it about the SCTP negotiated capabilities.
|
|
@@ -3033,7 +3010,7 @@ namespace RTC
|
|
|
3033
3010
|
|
|
3034
3011
|
// For debugging purposes.
|
|
3035
3012
|
#if MS_LOG_DEV_LEVEL == 3
|
|
3036
|
-
MS_DUMP("
|
|
3013
|
+
MS_DUMP("SCTP association connected:");
|
|
3037
3014
|
this->sctpAssociation->Dump();
|
|
3038
3015
|
#endif
|
|
3039
3016
|
}
|
|
@@ -3179,10 +3156,17 @@ namespace RTC
|
|
|
3179
3156
|
// Pass the SCTP message to the corresponding DataProducer.
|
|
3180
3157
|
try
|
|
3181
3158
|
{
|
|
3182
|
-
|
|
3159
|
+
std::vector<uint16_t> subchannels;
|
|
3160
|
+
std::optional<uint16_t> requiredSubchannel;
|
|
3161
|
+
|
|
3162
|
+
// When this is a pipe transport, the subchannels and required subchannel
|
|
3163
|
+
// may be encoded at the beginning of the message payload.
|
|
3164
|
+
if (this->IsPipe())
|
|
3165
|
+
{
|
|
3166
|
+
RTC::SubchannelsCodec::DecodeSubchannels(message, subchannels, requiredSubchannel);
|
|
3167
|
+
}
|
|
3183
3168
|
|
|
3184
|
-
dataProducer->ReceiveMessage(
|
|
3185
|
-
std::move(message), emptySubchannels, /*requiredSubchannel*/ std::nullopt);
|
|
3169
|
+
dataProducer->ReceiveMessage(std::move(message), subchannels, requiredSubchannel);
|
|
3186
3170
|
}
|
|
3187
3171
|
catch (std::exception& error)
|
|
3188
3172
|
{
|
|
@@ -3247,8 +3231,6 @@ namespace RTC
|
|
|
3247
3231
|
|
|
3248
3232
|
if (!dataConsumersToClose.empty())
|
|
3249
3233
|
{
|
|
3250
|
-
this->sctpAssociation->ResetStreams(streamsToReset);
|
|
3251
|
-
|
|
3252
3234
|
for (auto* dataConsumer : dataConsumersToClose)
|
|
3253
3235
|
{
|
|
3254
3236
|
// Remove it from the maps.
|
|
@@ -3271,6 +3253,12 @@ namespace RTC
|
|
|
3271
3253
|
// Delete it.
|
|
3272
3254
|
delete dataConsumer;
|
|
3273
3255
|
}
|
|
3256
|
+
|
|
3257
|
+
// NOTE: This must be called after removing data consumers from the maps,
|
|
3258
|
+
// otherwise if `OnAssociationStreamBufferedAmountLow()` was triggered it
|
|
3259
|
+
// would end up emitting an event associated to an already closed data
|
|
3260
|
+
// consumer.
|
|
3261
|
+
this->sctpAssociation->ResetStreams(streamsToReset);
|
|
3274
3262
|
}
|
|
3275
3263
|
}
|
|
3276
3264
|
}
|
|
@@ -3279,7 +3267,7 @@ namespace RTC
|
|
|
3279
3267
|
{
|
|
3280
3268
|
MS_TRACE();
|
|
3281
3269
|
|
|
3282
|
-
const auto* dataConsumer =
|
|
3270
|
+
const auto* dataConsumer = GetSctpDataConsumerByStreamId(streamId);
|
|
3283
3271
|
|
|
3284
3272
|
if (!dataConsumer)
|
|
3285
3273
|
{
|
package/worker/src/Worker.cpp
CHANGED
|
@@ -188,7 +188,7 @@ RTC::WebRtcServer* Worker::AssertAndGetWebRtcServerById(
|
|
|
188
188
|
{
|
|
189
189
|
MS_TRACE();
|
|
190
190
|
|
|
191
|
-
auto it = this->mapWebRtcServers.find(webRtcServerId);
|
|
191
|
+
const auto it = this->mapWebRtcServers.find(webRtcServerId);
|
|
192
192
|
|
|
193
193
|
if (it == this->mapWebRtcServers.end())
|
|
194
194
|
{
|
|
@@ -202,7 +202,7 @@ RTC::Router* Worker::AssertAndGetRouterById(const std::string& routerId, const s
|
|
|
202
202
|
{
|
|
203
203
|
MS_TRACE();
|
|
204
204
|
|
|
205
|
-
auto it = this->mapRouters.find(routerId);
|
|
205
|
+
const auto it = this->mapRouters.find(routerId);
|
|
206
206
|
|
|
207
207
|
if (it == this->mapRouters.end())
|
|
208
208
|
{
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#include "common.hpp"
|
|
2
|
+
#include "RTC/SCTP/public/Message.hpp"
|
|
3
|
+
#include "RTC/SubchannelsCodec.hpp"
|
|
4
|
+
#include "Utils.hpp"
|
|
5
|
+
#include <catch2/catch_test_macros.hpp>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
SCENARIO("SubchannelsCodec", "[subchannels]")
|
|
9
|
+
{
|
|
10
|
+
// Build a message with the given payload.
|
|
11
|
+
auto makeMessage = [](const std::vector<uint8_t>& payload)
|
|
12
|
+
{
|
|
13
|
+
return RTC::SCTP::Message(/*streamId*/ 1, /*ppid*/ 51, payload);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Copy the current payload of a message into a std::vector.
|
|
17
|
+
auto payloadToVector = [](const RTC::SCTP::Message& message)
|
|
18
|
+
{
|
|
19
|
+
const auto payload = message.GetPayload();
|
|
20
|
+
|
|
21
|
+
return std::vector<uint8_t>(payload.begin(), payload.end());
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
SECTION(
|
|
25
|
+
"EncodeSubchannels() encodes subchannels and requiredSubchannel and DecodeSubchannels() decodes them back")
|
|
26
|
+
{
|
|
27
|
+
const std::vector<uint8_t> originalPayload = { 0x01, 0x02, 0x03, 0x04 };
|
|
28
|
+
|
|
29
|
+
auto message = makeMessage(originalPayload);
|
|
30
|
+
|
|
31
|
+
const std::vector<uint16_t> subchannels = { 10, 20, 300 };
|
|
32
|
+
const std::optional<uint16_t> requiredSubchannel = 5;
|
|
33
|
+
|
|
34
|
+
REQUIRE(
|
|
35
|
+
RTC::SubchannelsCodec::EncodeSubchannels(message, subchannels, requiredSubchannel) == true);
|
|
36
|
+
|
|
37
|
+
// 8 (Magic Token) + 2 (subchannelsCount) + 3 * 2 (subchannels) + 2 (requiredSubchannel).
|
|
38
|
+
REQUIRE(message.GetPayloadLength() == originalPayload.size() + 8 + 2 + (3 * 2) + 2);
|
|
39
|
+
|
|
40
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
41
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
42
|
+
|
|
43
|
+
REQUIRE(
|
|
44
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
45
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == true);
|
|
46
|
+
|
|
47
|
+
REQUIRE(decodedSubchannels == subchannels);
|
|
48
|
+
REQUIRE(decodedRequiredSubchannel == requiredSubchannel);
|
|
49
|
+
// The header must have been removed, leaving the original payload.
|
|
50
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
SECTION("EncodeSubchannels() encodes subchannels without requiredSubchannel")
|
|
54
|
+
{
|
|
55
|
+
const std::vector<uint8_t> originalPayload = { 0xAA, 0xBB };
|
|
56
|
+
|
|
57
|
+
auto message = makeMessage(originalPayload);
|
|
58
|
+
|
|
59
|
+
const std::vector<uint16_t> subchannels = { 1, 2 };
|
|
60
|
+
|
|
61
|
+
REQUIRE(RTC::SubchannelsCodec::EncodeSubchannels(message, subchannels, std::nullopt) == true);
|
|
62
|
+
|
|
63
|
+
// 8 (Magic Token) + 2 (subchannelsCount) + 2 * 2 (subchannels).
|
|
64
|
+
REQUIRE(message.GetPayloadLength() == originalPayload.size() + 8 + 2 + (2 * 2));
|
|
65
|
+
|
|
66
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
67
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
68
|
+
|
|
69
|
+
REQUIRE(
|
|
70
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
71
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == true);
|
|
72
|
+
|
|
73
|
+
REQUIRE(decodedSubchannels == subchannels);
|
|
74
|
+
REQUIRE(decodedRequiredSubchannel.has_value() == false);
|
|
75
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
SECTION("EncodeSubchannels() encodes requiredSubchannel without subchannels")
|
|
79
|
+
{
|
|
80
|
+
const std::vector<uint8_t> originalPayload = { 0xAA, 0xBB, 0xCC };
|
|
81
|
+
|
|
82
|
+
auto message = makeMessage(originalPayload);
|
|
83
|
+
|
|
84
|
+
const std::optional<uint16_t> requiredSubchannel = 4321;
|
|
85
|
+
|
|
86
|
+
REQUIRE(RTC::SubchannelsCodec::EncodeSubchannels(message, {}, requiredSubchannel) == true);
|
|
87
|
+
|
|
88
|
+
// 8 (Magic Token) + 2 (subchannelsCount) + 2 (requiredSubchannel).
|
|
89
|
+
REQUIRE(message.GetPayloadLength() == originalPayload.size() + 8 + 2 + 2);
|
|
90
|
+
|
|
91
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
92
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
93
|
+
|
|
94
|
+
REQUIRE(
|
|
95
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
96
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == true);
|
|
97
|
+
|
|
98
|
+
REQUIRE(decodedSubchannels.empty() == true);
|
|
99
|
+
REQUIRE(decodedRequiredSubchannel == requiredSubchannel);
|
|
100
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
SECTION("EncodeSubchannels() does nothing if there is nothing to encode")
|
|
104
|
+
{
|
|
105
|
+
const std::vector<uint8_t> originalPayload = { 0x01, 0x02 };
|
|
106
|
+
|
|
107
|
+
auto message = makeMessage(originalPayload);
|
|
108
|
+
|
|
109
|
+
REQUIRE(RTC::SubchannelsCodec::EncodeSubchannels(message, {}, std::nullopt) == false);
|
|
110
|
+
|
|
111
|
+
// The message must remain untouched.
|
|
112
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
SECTION("DecodeSubchannels() does nothing if the message does not start with the Magic Token")
|
|
116
|
+
{
|
|
117
|
+
const std::vector<uint8_t> originalPayload(12);
|
|
118
|
+
|
|
119
|
+
auto message = makeMessage(originalPayload);
|
|
120
|
+
|
|
121
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
122
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
123
|
+
|
|
124
|
+
REQUIRE(
|
|
125
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
126
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == false);
|
|
127
|
+
|
|
128
|
+
REQUIRE(decodedSubchannels.empty() == true);
|
|
129
|
+
REQUIRE(decodedRequiredSubchannel.has_value() == false);
|
|
130
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
SECTION("DecodeSubchannels() does nothing if the message is shorter than the Magic Token")
|
|
134
|
+
{
|
|
135
|
+
const std::vector<uint8_t> originalPayload = { 0x01, 0x02 };
|
|
136
|
+
|
|
137
|
+
auto message = makeMessage(originalPayload);
|
|
138
|
+
|
|
139
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
140
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
141
|
+
|
|
142
|
+
REQUIRE(
|
|
143
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
144
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == false);
|
|
145
|
+
|
|
146
|
+
REQUIRE(decodedSubchannels.empty() == true);
|
|
147
|
+
REQUIRE(decodedRequiredSubchannel.has_value() == false);
|
|
148
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
SECTION(
|
|
152
|
+
"DecodeSubchannels() ignores a truncated message that announces more subchannels than present")
|
|
153
|
+
{
|
|
154
|
+
// Craft a payload with the Magic Token (no requiredSubchannelFlag) that
|
|
155
|
+
// announces 3 subchannels but does not carry their bytes.
|
|
156
|
+
std::vector<uint8_t> originalPayload(10);
|
|
157
|
+
|
|
158
|
+
Utils::Byte::Set8Bytes(originalPayload.data(), 0, RTC::SubchannelsCodec::MagicToken);
|
|
159
|
+
Utils::Byte::Set2Bytes(originalPayload.data(), 8, /*subchannelsCount*/ 3);
|
|
160
|
+
|
|
161
|
+
auto message = makeMessage(originalPayload);
|
|
162
|
+
|
|
163
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
164
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
165
|
+
|
|
166
|
+
REQUIRE(
|
|
167
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
168
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == false);
|
|
169
|
+
|
|
170
|
+
REQUIRE(decodedSubchannels.empty() == true);
|
|
171
|
+
REQUIRE(decodedRequiredSubchannel.has_value() == false);
|
|
172
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
SECTION(
|
|
176
|
+
"DecodeSubchannels() ignores a message whose requiredSubchannelFlag is set but has no room for the requiredSubchannel field")
|
|
177
|
+
{
|
|
178
|
+
// Craft a payload with the Magic Token and requiredSubchannelFlag set,
|
|
179
|
+
// announcing 1 subchannel (present) but without the requiredSubchannel bytes.
|
|
180
|
+
std::vector<uint8_t> originalPayload(12);
|
|
181
|
+
|
|
182
|
+
Utils::Byte::Set8Bytes(
|
|
183
|
+
originalPayload.data(),
|
|
184
|
+
0,
|
|
185
|
+
RTC::SubchannelsCodec::MagicToken | RTC::SubchannelsCodec::RequiredSubchannelFlagMask);
|
|
186
|
+
Utils::Byte::Set2Bytes(originalPayload.data(), 8, /*subchannelsCount*/ 1);
|
|
187
|
+
Utils::Byte::Set2Bytes(originalPayload.data(), 10, /*subchannel*/ 42);
|
|
188
|
+
|
|
189
|
+
auto message = makeMessage(originalPayload);
|
|
190
|
+
|
|
191
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
192
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
193
|
+
|
|
194
|
+
REQUIRE(
|
|
195
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
196
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == false);
|
|
197
|
+
|
|
198
|
+
REQUIRE(decodedSubchannels.empty() == true);
|
|
199
|
+
REQUIRE(decodedRequiredSubchannel.has_value() == false);
|
|
200
|
+
REQUIRE(payloadToVector(message) == originalPayload);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
SECTION("EncodeSubchannels() and DecodeSubchannels() work with an empty original payload")
|
|
204
|
+
{
|
|
205
|
+
auto message = makeMessage({});
|
|
206
|
+
|
|
207
|
+
const std::vector<uint16_t> subchannels = { 7 };
|
|
208
|
+
const std::optional<uint16_t> requiredSubchannel = 7;
|
|
209
|
+
|
|
210
|
+
REQUIRE(
|
|
211
|
+
RTC::SubchannelsCodec::EncodeSubchannels(message, subchannels, requiredSubchannel) == true);
|
|
212
|
+
|
|
213
|
+
std::vector<uint16_t> decodedSubchannels;
|
|
214
|
+
std::optional<uint16_t> decodedRequiredSubchannel;
|
|
215
|
+
|
|
216
|
+
REQUIRE(
|
|
217
|
+
RTC::SubchannelsCodec::DecodeSubchannels(
|
|
218
|
+
message, decodedSubchannels, decodedRequiredSubchannel) == true);
|
|
219
|
+
|
|
220
|
+
REQUIRE(decodedSubchannels == subchannels);
|
|
221
|
+
REQUIRE(decodedRequiredSubchannel == requiredSubchannel);
|
|
222
|
+
REQUIRE(message.GetPayloadLength() == 0);
|
|
223
|
+
}
|
|
224
|
+
}
|