@webex/plugin-meetings 3.12.0-next.41 → 3.12.0-next.43
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/dist/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/media/properties.js +1 -0
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +37 -6
- package/dist/meeting/index.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/media/properties.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +3 -3
- package/src/constants.ts +1 -0
- package/src/media/properties.ts +1 -0
- package/src/meeting/index.ts +27 -0
- package/test/unit/spec/meeting/index.js +291 -0
|
@@ -293,6 +293,7 @@ export declare const EVENT_TRIGGERS: {
|
|
|
293
293
|
MEETING_CAPTION_RECEIVED: string;
|
|
294
294
|
MEETING_PARTICIPANT_REASON_CHANGED: string;
|
|
295
295
|
MEETING_AI_ENABLE_REQUEST: string;
|
|
296
|
+
MEETING_SRTP_CIPHER_UPDATED: string;
|
|
296
297
|
};
|
|
297
298
|
export declare const EVENT_TYPES: {
|
|
298
299
|
SELF: string;
|
package/dist/webinar/index.js
CHANGED
package/package.json
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@webex/common": "3.12.0-next.1",
|
|
65
|
-
"@webex/internal-media-core": "2.
|
|
65
|
+
"@webex/internal-media-core": "2.24.1",
|
|
66
66
|
"@webex/internal-plugin-conversation": "3.12.0-next.16",
|
|
67
67
|
"@webex/internal-plugin-device": "3.12.0-next.14",
|
|
68
68
|
"@webex/internal-plugin-llm": "3.12.0-next.16",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@webex/internal-plugin-support": "3.12.0-next.16",
|
|
72
72
|
"@webex/internal-plugin-user": "3.12.0-next.15",
|
|
73
73
|
"@webex/internal-plugin-voicea": "3.12.0-next.16",
|
|
74
|
-
"@webex/media-helpers": "3.12.0-next.
|
|
74
|
+
"@webex/media-helpers": "3.12.0-next.3",
|
|
75
75
|
"@webex/plugin-people": "3.12.0-next.15",
|
|
76
76
|
"@webex/plugin-rooms": "3.12.0-next.16",
|
|
77
77
|
"@webex/ts-sdp": "^1.8.1",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"//": [
|
|
95
95
|
"TODO: upgrade jwt-decode when moving to node 18"
|
|
96
96
|
],
|
|
97
|
-
"version": "3.12.0-next.
|
|
97
|
+
"version": "3.12.0-next.43"
|
|
98
98
|
}
|
package/src/constants.ts
CHANGED
|
@@ -391,6 +391,7 @@ export const EVENT_TRIGGERS = {
|
|
|
391
391
|
MEETING_CAPTION_RECEIVED: 'meeting:caption-received',
|
|
392
392
|
MEETING_PARTICIPANT_REASON_CHANGED: 'meeting:participant-reason-changed',
|
|
393
393
|
MEETING_AI_ENABLE_REQUEST: 'meeting:aiEnableRequest',
|
|
394
|
+
MEETING_SRTP_CIPHER_UPDATED: 'meeting:srtpCipher:updated',
|
|
394
395
|
};
|
|
395
396
|
|
|
396
397
|
export const EVENT_TYPES = {
|
package/src/media/properties.ts
CHANGED
|
@@ -43,6 +43,7 @@ export default class MediaProperties {
|
|
|
43
43
|
shareAudioStream?: LocalSystemAudioStream;
|
|
44
44
|
videoDeviceId: any;
|
|
45
45
|
videoStream?: LocalCameraStream;
|
|
46
|
+
srtpCipher: string | undefined;
|
|
46
47
|
namespace = MEETINGS;
|
|
47
48
|
mediaIssueCounters: {[key: string]: number} = {};
|
|
48
49
|
throttledSendMediaIssueMetric: ReturnType<typeof throttle>;
|
package/src/meeting/index.ts
CHANGED
|
@@ -7603,6 +7603,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7603
7603
|
}
|
|
7604
7604
|
}
|
|
7605
7605
|
});
|
|
7606
|
+
this.statsAnalyzer.on(StatsAnalyzerEventNames.STATS_UPDATE, (data) => {
|
|
7607
|
+
// Extract srtpCipher from transport stats
|
|
7608
|
+
let srtpCipher: string | undefined;
|
|
7609
|
+
for (const stats of data.stats.values()) {
|
|
7610
|
+
if (stats.type === 'transport' && stats.srtpCipher) {
|
|
7611
|
+
srtpCipher = stats.srtpCipher as string;
|
|
7612
|
+
break;
|
|
7613
|
+
}
|
|
7614
|
+
}
|
|
7615
|
+
|
|
7616
|
+
// Only emit event if srtpCipher has changed
|
|
7617
|
+
if (srtpCipher && srtpCipher !== this.mediaProperties.srtpCipher) {
|
|
7618
|
+
LoggerProxy.logger.info(
|
|
7619
|
+
`Meeting:index#setupStatsAnalyzerEventHandlers --> SRTP cipher changed from ${this.mediaProperties.srtpCipher} to ${srtpCipher}`
|
|
7620
|
+
);
|
|
7621
|
+
this.mediaProperties.srtpCipher = srtpCipher;
|
|
7622
|
+
Trigger.trigger(
|
|
7623
|
+
this,
|
|
7624
|
+
{
|
|
7625
|
+
file: 'meeting/index',
|
|
7626
|
+
function: 'setupStatsAnalyzerEventHandlers',
|
|
7627
|
+
},
|
|
7628
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
7629
|
+
{srtpCipher}
|
|
7630
|
+
);
|
|
7631
|
+
}
|
|
7632
|
+
});
|
|
7606
7633
|
};
|
|
7607
7634
|
|
|
7608
7635
|
getMediaConnectionDebugId() {
|
|
@@ -4535,6 +4535,297 @@ describe('plugin-meetings', () => {
|
|
|
4535
4535
|
},
|
|
4536
4536
|
});
|
|
4537
4537
|
});
|
|
4538
|
+
|
|
4539
|
+
describe('handles STATS_UPDATE event for SRTP cipher detection', () => {
|
|
4540
|
+
it('emits MEETING_SRTP_CIPHER_UPDATED event when srtpCipher is found in transport stats', async () => {
|
|
4541
|
+
const fakeStats = new Map([
|
|
4542
|
+
[
|
|
4543
|
+
'transport-1',
|
|
4544
|
+
{
|
|
4545
|
+
type: 'transport',
|
|
4546
|
+
srtpCipher: 'AES_CM_128_HMAC_SHA1_80',
|
|
4547
|
+
dtlsCipher: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
|
|
4548
|
+
},
|
|
4549
|
+
],
|
|
4550
|
+
[
|
|
4551
|
+
'outbound-rtp-1',
|
|
4552
|
+
{
|
|
4553
|
+
type: 'outbound-rtp',
|
|
4554
|
+
ssrc: 12345,
|
|
4555
|
+
},
|
|
4556
|
+
],
|
|
4557
|
+
]);
|
|
4558
|
+
|
|
4559
|
+
statsAnalyzerStub.emit(
|
|
4560
|
+
{file: 'test', function: 'test'},
|
|
4561
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4562
|
+
{stats: fakeStats}
|
|
4563
|
+
);
|
|
4564
|
+
|
|
4565
|
+
assert.calledWith(
|
|
4566
|
+
TriggerProxy.trigger,
|
|
4567
|
+
sinon.match.instanceOf(Meeting),
|
|
4568
|
+
{
|
|
4569
|
+
file: 'meeting/index',
|
|
4570
|
+
function: 'setupStatsAnalyzerEventHandlers',
|
|
4571
|
+
},
|
|
4572
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4573
|
+
{srtpCipher: 'AES_CM_128_HMAC_SHA1_80'}
|
|
4574
|
+
);
|
|
4575
|
+
|
|
4576
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AES_CM_128_HMAC_SHA1_80');
|
|
4577
|
+
});
|
|
4578
|
+
|
|
4579
|
+
it('updates meeting.mediaProperties.srtpCipher when cipher changes', async () => {
|
|
4580
|
+
const firstStats = new Map([
|
|
4581
|
+
[
|
|
4582
|
+
'transport-1',
|
|
4583
|
+
{
|
|
4584
|
+
type: 'transport',
|
|
4585
|
+
srtpCipher: 'AES_CM_128_HMAC_SHA1_80',
|
|
4586
|
+
},
|
|
4587
|
+
],
|
|
4588
|
+
]);
|
|
4589
|
+
|
|
4590
|
+
statsAnalyzerStub.emit(
|
|
4591
|
+
{file: 'test', function: 'test'},
|
|
4592
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4593
|
+
{stats: firstStats}
|
|
4594
|
+
);
|
|
4595
|
+
|
|
4596
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AES_CM_128_HMAC_SHA1_80');
|
|
4597
|
+
|
|
4598
|
+
const secondStats = new Map([
|
|
4599
|
+
[
|
|
4600
|
+
'transport-1',
|
|
4601
|
+
{
|
|
4602
|
+
type: 'transport',
|
|
4603
|
+
srtpCipher: 'AEAD_AES_256_GCM',
|
|
4604
|
+
},
|
|
4605
|
+
],
|
|
4606
|
+
]);
|
|
4607
|
+
|
|
4608
|
+
TriggerProxy.trigger.resetHistory();
|
|
4609
|
+
|
|
4610
|
+
statsAnalyzerStub.emit(
|
|
4611
|
+
{file: 'test', function: 'test'},
|
|
4612
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4613
|
+
{stats: secondStats}
|
|
4614
|
+
);
|
|
4615
|
+
|
|
4616
|
+
assert.calledWith(
|
|
4617
|
+
TriggerProxy.trigger,
|
|
4618
|
+
sinon.match.instanceOf(Meeting),
|
|
4619
|
+
{
|
|
4620
|
+
file: 'meeting/index',
|
|
4621
|
+
function: 'setupStatsAnalyzerEventHandlers',
|
|
4622
|
+
},
|
|
4623
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4624
|
+
{srtpCipher: 'AEAD_AES_256_GCM'}
|
|
4625
|
+
);
|
|
4626
|
+
|
|
4627
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AEAD_AES_256_GCM');
|
|
4628
|
+
});
|
|
4629
|
+
|
|
4630
|
+
it('does not emit event when srtpCipher has not changed', async () => {
|
|
4631
|
+
const firstStats = new Map([
|
|
4632
|
+
[
|
|
4633
|
+
'transport-1',
|
|
4634
|
+
{
|
|
4635
|
+
type: 'transport',
|
|
4636
|
+
srtpCipher: 'AES_CM_128_HMAC_SHA1_80',
|
|
4637
|
+
},
|
|
4638
|
+
],
|
|
4639
|
+
]);
|
|
4640
|
+
|
|
4641
|
+
statsAnalyzerStub.emit(
|
|
4642
|
+
{file: 'test', function: 'test'},
|
|
4643
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4644
|
+
{stats: firstStats}
|
|
4645
|
+
);
|
|
4646
|
+
|
|
4647
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AES_CM_128_HMAC_SHA1_80');
|
|
4648
|
+
|
|
4649
|
+
TriggerProxy.trigger.resetHistory();
|
|
4650
|
+
|
|
4651
|
+
// Emit same cipher again
|
|
4652
|
+
statsAnalyzerStub.emit(
|
|
4653
|
+
{file: 'test', function: 'test'},
|
|
4654
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4655
|
+
{stats: firstStats}
|
|
4656
|
+
);
|
|
4657
|
+
|
|
4658
|
+
// Should not trigger event again
|
|
4659
|
+
assert.neverCalledWith(
|
|
4660
|
+
TriggerProxy.trigger,
|
|
4661
|
+
sinon.match.instanceOf(Meeting),
|
|
4662
|
+
sinon.match.any,
|
|
4663
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4664
|
+
sinon.match.any
|
|
4665
|
+
);
|
|
4666
|
+
|
|
4667
|
+
// Cipher should remain the same
|
|
4668
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AES_CM_128_HMAC_SHA1_80');
|
|
4669
|
+
});
|
|
4670
|
+
|
|
4671
|
+
it('does not emit event when stats contain no transport with srtpCipher', async () => {
|
|
4672
|
+
const fakeStats = new Map([
|
|
4673
|
+
[
|
|
4674
|
+
'outbound-rtp-1',
|
|
4675
|
+
{
|
|
4676
|
+
type: 'outbound-rtp',
|
|
4677
|
+
ssrc: 12345,
|
|
4678
|
+
},
|
|
4679
|
+
],
|
|
4680
|
+
[
|
|
4681
|
+
'inbound-rtp-1',
|
|
4682
|
+
{
|
|
4683
|
+
type: 'inbound-rtp',
|
|
4684
|
+
ssrc: 67890,
|
|
4685
|
+
},
|
|
4686
|
+
],
|
|
4687
|
+
]);
|
|
4688
|
+
|
|
4689
|
+
statsAnalyzerStub.emit(
|
|
4690
|
+
{file: 'test', function: 'test'},
|
|
4691
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4692
|
+
{stats: fakeStats}
|
|
4693
|
+
);
|
|
4694
|
+
|
|
4695
|
+
assert.neverCalledWith(
|
|
4696
|
+
TriggerProxy.trigger,
|
|
4697
|
+
sinon.match.instanceOf(Meeting),
|
|
4698
|
+
sinon.match.any,
|
|
4699
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4700
|
+
sinon.match.any
|
|
4701
|
+
);
|
|
4702
|
+
|
|
4703
|
+
assert.isUndefined(meeting.mediaProperties.srtpCipher);
|
|
4704
|
+
});
|
|
4705
|
+
|
|
4706
|
+
it('does not emit event when transport stat has no srtpCipher property', async () => {
|
|
4707
|
+
const fakeStats = new Map([
|
|
4708
|
+
[
|
|
4709
|
+
'transport-1',
|
|
4710
|
+
{
|
|
4711
|
+
type: 'transport',
|
|
4712
|
+
dtlsCipher: 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256',
|
|
4713
|
+
// no srtpCipher property
|
|
4714
|
+
},
|
|
4715
|
+
],
|
|
4716
|
+
]);
|
|
4717
|
+
|
|
4718
|
+
statsAnalyzerStub.emit(
|
|
4719
|
+
{file: 'test', function: 'test'},
|
|
4720
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4721
|
+
{stats: fakeStats}
|
|
4722
|
+
);
|
|
4723
|
+
|
|
4724
|
+
assert.neverCalledWith(
|
|
4725
|
+
TriggerProxy.trigger,
|
|
4726
|
+
sinon.match.instanceOf(Meeting),
|
|
4727
|
+
sinon.match.any,
|
|
4728
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4729
|
+
sinon.match.any
|
|
4730
|
+
);
|
|
4731
|
+
|
|
4732
|
+
assert.isUndefined(meeting.mediaProperties.srtpCipher);
|
|
4733
|
+
});
|
|
4734
|
+
|
|
4735
|
+
it('uses first transport with srtpCipher when multiple transports exist', async () => {
|
|
4736
|
+
const fakeStats = new Map([
|
|
4737
|
+
[
|
|
4738
|
+
'transport-1',
|
|
4739
|
+
{
|
|
4740
|
+
type: 'transport',
|
|
4741
|
+
srtpCipher: 'AES_CM_128_HMAC_SHA1_80',
|
|
4742
|
+
},
|
|
4743
|
+
],
|
|
4744
|
+
[
|
|
4745
|
+
'transport-2',
|
|
4746
|
+
{
|
|
4747
|
+
type: 'transport',
|
|
4748
|
+
srtpCipher: 'AEAD_AES_256_GCM',
|
|
4749
|
+
},
|
|
4750
|
+
],
|
|
4751
|
+
[
|
|
4752
|
+
'outbound-rtp-1',
|
|
4753
|
+
{
|
|
4754
|
+
type: 'outbound-rtp',
|
|
4755
|
+
ssrc: 12345,
|
|
4756
|
+
},
|
|
4757
|
+
],
|
|
4758
|
+
]);
|
|
4759
|
+
|
|
4760
|
+
statsAnalyzerStub.emit(
|
|
4761
|
+
{file: 'test', function: 'test'},
|
|
4762
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4763
|
+
{stats: fakeStats}
|
|
4764
|
+
);
|
|
4765
|
+
|
|
4766
|
+
assert.calledWith(
|
|
4767
|
+
TriggerProxy.trigger,
|
|
4768
|
+
sinon.match.instanceOf(Meeting),
|
|
4769
|
+
{
|
|
4770
|
+
file: 'meeting/index',
|
|
4771
|
+
function: 'setupStatsAnalyzerEventHandlers',
|
|
4772
|
+
},
|
|
4773
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4774
|
+
{srtpCipher: 'AES_CM_128_HMAC_SHA1_80'}
|
|
4775
|
+
);
|
|
4776
|
+
|
|
4777
|
+
assert.equal(meeting.mediaProperties.srtpCipher, 'AES_CM_128_HMAC_SHA1_80');
|
|
4778
|
+
});
|
|
4779
|
+
|
|
4780
|
+
it('handles empty stats map without errors', async () => {
|
|
4781
|
+
const emptyStats = new Map();
|
|
4782
|
+
|
|
4783
|
+
statsAnalyzerStub.emit(
|
|
4784
|
+
{file: 'test', function: 'test'},
|
|
4785
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4786
|
+
{stats: emptyStats}
|
|
4787
|
+
);
|
|
4788
|
+
|
|
4789
|
+
assert.neverCalledWith(
|
|
4790
|
+
TriggerProxy.trigger,
|
|
4791
|
+
sinon.match.instanceOf(Meeting),
|
|
4792
|
+
sinon.match.any,
|
|
4793
|
+
EVENT_TRIGGERS.MEETING_SRTP_CIPHER_UPDATED,
|
|
4794
|
+
sinon.match.any
|
|
4795
|
+
);
|
|
4796
|
+
|
|
4797
|
+
assert.isUndefined(meeting.mediaProperties.srtpCipher);
|
|
4798
|
+
});
|
|
4799
|
+
|
|
4800
|
+
it('logs cipher change when cipher is updated', async () => {
|
|
4801
|
+
const loggerSpy = sinon.spy(LoggerProxy.logger, 'info');
|
|
4802
|
+
|
|
4803
|
+
meeting.mediaProperties.srtpCipher = 'AES_CM_128_HMAC_SHA1_80';
|
|
4804
|
+
|
|
4805
|
+
const newStats = new Map([
|
|
4806
|
+
[
|
|
4807
|
+
'transport-1',
|
|
4808
|
+
{
|
|
4809
|
+
type: 'transport',
|
|
4810
|
+
srtpCipher: 'AEAD_AES_256_GCM',
|
|
4811
|
+
},
|
|
4812
|
+
],
|
|
4813
|
+
]);
|
|
4814
|
+
|
|
4815
|
+
statsAnalyzerStub.emit(
|
|
4816
|
+
{file: 'test', function: 'test'},
|
|
4817
|
+
StatsAnalyzerEventNames.STATS_UPDATE,
|
|
4818
|
+
{stats: newStats}
|
|
4819
|
+
);
|
|
4820
|
+
|
|
4821
|
+
assert.calledWithMatch(
|
|
4822
|
+
loggerSpy,
|
|
4823
|
+
sinon.match(/SRTP cipher changed from AES_CM_128_HMAC_SHA1_80 to AEAD_AES_256_GCM/)
|
|
4824
|
+
);
|
|
4825
|
+
|
|
4826
|
+
loggerSpy.restore();
|
|
4827
|
+
});
|
|
4828
|
+
});
|
|
4538
4829
|
});
|
|
4539
4830
|
|
|
4540
4831
|
describe('handles StatsMonitor events', () => {
|