diff --git a/docs/samples/browser-plugin-meetings/app.js b/docs/samples/browser-plugin-meetings/app.js index 04e4d41dfd0..4745945daab 100644 --- a/docs/samples/browser-plugin-meetings/app.js +++ b/docs/samples/browser-plugin-meetings/app.js @@ -38,7 +38,7 @@ const breakoutsList = document.getElementById('breakouts-list'); const breakoutTable = document.getElementById('breakout-table'); const breakoutHostOperation = document.getElementById('breakout-host-operation'); const getStatsButton = document.getElementById('get-stats'); -const tcpReachabilityConfigElm = document.getElementById('enable-tcp-reachability'); +const tcpReachabilityConfigElm = document.getElementById('enable-tcp-reachability'); const tlsReachabilityConfigElm = document.getElementById('enable-tls-reachability'); const guestName = document.querySelector('#guest-name'); @@ -388,7 +388,7 @@ createMeetingSelectElm.addEventListener('change', (event) => { } else { notes.classList.add('hidden'); - + } }); @@ -950,7 +950,7 @@ function cleanUpMedia() { elem.srcObject.getTracks().forEach((track) => track.stop()); // eslint-disable-next-line no-param-reassign elem.srcObject = null; - + if(elem.id === "local-video") { clearVideoResolutionCheckInterval(localVideoResElm, localVideoResolutionInterval); } @@ -1566,7 +1566,7 @@ async function stopStartVideo() { console.error(error); } } - + } async function stopStartAudio() { @@ -1608,7 +1608,7 @@ async function stopStartAudio() { console.error(error); } } - + } function populateSourceDevices(mediaDevice) { @@ -3330,6 +3330,23 @@ function toggleBreakout() { } } +async function toggleBrb() { + const meeting = getCurrentMeeting(); + + if (meeting) { + const enabled = document.getElementById('brb').checked; + try { + const result = await meeting.beRightBack(enabled); + console.log(`meeting.beRightBack(${enabled}): success. Result: ${result}`); + } catch (error) { + console.error(`meeting.beRightBack({${enabled}): error: `, error); + } finally { + localMedia?.microphoneStream?.setUserMuted(enabled); + localMedia?.cameraStream?.setUserMuted(enabled); + } + } +} + const createAdmitDiv = () => { const containerDiv = document.createElement('div'); diff --git a/docs/samples/browser-plugin-meetings/index.html b/docs/samples/browser-plugin-meetings/index.html index a994bf11ed5..f6cad1470b0 100644 --- a/docs/samples/browser-plugin-meetings/index.html +++ b/docs/samples/browser-plugin-meetings/index.html @@ -217,6 +217,14 @@

+
+
+ Step Away + +
+
+
+ diff --git a/packages/@webex/media-helpers/package.json b/packages/@webex/media-helpers/package.json index 8a91c6266bf..d42d1a4e2fd 100644 --- a/packages/@webex/media-helpers/package.json +++ b/packages/@webex/media-helpers/package.json @@ -22,7 +22,7 @@ "deploy:npm": "yarn npm publish" }, "dependencies": { - "@webex/internal-media-core": "2.11.3", + "@webex/internal-media-core": "2.12.0", "@webex/ts-events": "^1.1.0", "@webex/web-media-effects": "2.19.0" }, diff --git a/packages/@webex/plugin-meetings/package.json b/packages/@webex/plugin-meetings/package.json index 9772e87d059..acb8d59524d 100644 --- a/packages/@webex/plugin-meetings/package.json +++ b/packages/@webex/plugin-meetings/package.json @@ -62,7 +62,7 @@ }, "dependencies": { "@webex/common": "workspace:*", - "@webex/internal-media-core": "2.11.3", + "@webex/internal-media-core": "2.12.0", "@webex/internal-plugin-conversation": "workspace:*", "@webex/internal-plugin-device": "workspace:*", "@webex/internal-plugin-llm": "workspace:*", diff --git a/packages/@webex/plugin-meetings/src/constants.ts b/packages/@webex/plugin-meetings/src/constants.ts index 5e357a2263c..7ca7b0d3a6e 100644 --- a/packages/@webex/plugin-meetings/src/constants.ts +++ b/packages/@webex/plugin-meetings/src/constants.ts @@ -303,6 +303,7 @@ export const EVENT_TRIGGERS = { MEETING_SELF_CANNOT_VIEW_PARTICIPANT_LIST: 'meeting:self:cannotViewParticipantList', MEETING_SELF_IS_SHARING_BLOCKED: 'meeting:self:isSharingBlocked', MEETING_SELF_ROLES_CHANGED: 'meeting:self:rolesChanged', + MEETING_SELF_BRB_UPDATE: 'meeting:self:brbUpdate', MEETING_CONTROLS_LAYOUT_UPDATE: 'meeting:layout:update', MEETING_ENTRY_EXIT_TONE_UPDATE: 'meeting:entryExitTone:update', MEETING_BREAKOUTS_UPDATE: 'meeting:breakouts:update', @@ -700,6 +701,7 @@ export const LOCUSINFO = { SELF_IS_SHARING_BLOCKED_CHANGE: 'SELF_IS_SHARING_BLOCKED_CHANGE', SELF_MEETING_BREAKOUTS_CHANGED: 'SELF_MEETING_BREAKOUTS_CHANGED', SELF_MEETING_INTERPRETATION_CHANGED: 'SELF_MEETING_INTERPRETATION_CHANGED', + SELF_MEETING_BRB_CHANGED: 'SELF_MEETING_BRB_CHANGED', MEDIA_INACTIVITY: 'MEDIA_INACTIVITY', LINKS_SERVICES: 'LINKS_SERVICES', }, diff --git a/packages/@webex/plugin-meetings/src/locus-info/index.ts b/packages/@webex/plugin-meetings/src/locus-info/index.ts index 489d12fda57..8fc8626ac21 100644 --- a/packages/@webex/plugin-meetings/src/locus-info/index.ts +++ b/packages/@webex/plugin-meetings/src/locus-info/index.ts @@ -1333,6 +1333,19 @@ export default class LocusInfo extends EventsScope { ); } + if (parsedSelves.updates.brbChanged) { + this.emitScoped( + { + file: 'locus-info', + function: 'updateSelf', + }, + LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, + { + brb: parsedSelves.current.brb, + } + ); + } + if (parsedSelves.updates.interpretationChanged) { this.emitScoped( { diff --git a/packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts b/packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts index 0ba3e4d8178..d27f32e47d5 100644 --- a/packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts +++ b/packages/@webex/plugin-meetings/src/locus-info/selfUtils.ts @@ -66,6 +66,7 @@ SelfUtils.parse = (self: any, deviceId: string) => { breakoutSessions: SelfUtils.getBreakoutSessions(self), breakout: SelfUtils.getBreakout(self), interpretation: SelfUtils.getInterpretation(self), + brb: SelfUtils.getBrb(self), }; } @@ -75,6 +76,7 @@ SelfUtils.parse = (self: any, deviceId: string) => { SelfUtils.getBreakoutSessions = (self) => self?.controls?.breakout?.sessions; SelfUtils.getBreakout = (self) => self?.controls?.breakout; SelfUtils.getInterpretation = (self) => self?.controls?.interpretation; +SelfUtils.getBrb = (self) => self?.controls?.brb; SelfUtils.getLayout = (self) => Array.isArray(self?.controls?.layouts) ? self.controls.layouts[0].type : undefined; @@ -128,6 +130,7 @@ SelfUtils.getSelves = (oldSelf, newSelf, deviceId) => { updates.isSharingBlockedChanged = previous?.isSharingBlocked !== current.isSharingBlocked; updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current); updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current); + updates.brbChanged = SelfUtils.brbChanged(previous, current); return { previous, @@ -159,6 +162,9 @@ SelfUtils.breakoutsChanged = (previous, current) => SelfUtils.interpretationChanged = (previous, current) => !isEqual(previous?.interpretation, current?.interpretation) && !!current?.interpretation; +SelfUtils.brbChanged = (previous, current) => + !isEqual(previous?.brb, current?.brb) && current?.brb !== undefined; + SelfUtils.isMediaInactive = (previous, current) => { if ( previous && diff --git a/packages/@webex/plugin-meetings/src/meeting/index.ts b/packages/@webex/plugin-meetings/src/meeting/index.ts index b11fd127e64..075d1a95737 100644 --- a/packages/@webex/plugin-meetings/src/meeting/index.ts +++ b/packages/@webex/plugin-meetings/src/meeting/index.ts @@ -3303,6 +3303,20 @@ export default class Meeting extends StatelessWebexPlugin { } }); + this.locusInfo.on(LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, (payload) => { + Trigger.trigger( + this, + { + file: 'meeting/index', + function: 'setUpLocusInfoSelfListener', + }, + EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE, + { + payload, + } + ); + }); + this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ROLES_CHANGED, (payload) => { const isModeratorOrCohost = payload.newRoles?.includes(SELF_ROLES.MODERATOR) || @@ -3505,6 +3519,50 @@ export default class Meeting extends StatelessWebexPlugin { return this.members.admitMembers(memberIds, locusUrls); } + /** + * Manages be right back status updates for the current participant. + * + * @param {boolean} enabled - Indicates whether the user enabled brb or not. + * @returns {Promise} resolves when the brb status is updated or does nothing if not in a multistream meeting. + * @throws {Error} - Throws an error if the request fails. + */ + public async beRightBack(enabled: boolean): Promise { + if (!this.isMultistream) { + const errorMessage = 'Meeting:index#beRightBack --> Not a multistream meeting'; + const error = new Error(errorMessage); + + LoggerProxy.logger.error(error); + + return Promise.reject(error); + } + + if (!this.mediaProperties.webrtcMediaConnection) { + const errorMessage = 'Meeting:index#beRightBack --> WebRTC media connection is not defined'; + const error = new Error(errorMessage); + + LoggerProxy.logger.error(error); + + return Promise.reject(error); + } + + // this logic should be applied only to multistream meetings + return this.meetingRequest + .sendBrb({ + enabled, + locusUrl: this.locusUrl, + deviceUrl: this.deviceUrl, + selfId: this.selfId, + }) + .then(() => { + this.sendSlotManager.setSourceStateOverride(MediaType.VideoMain, enabled ? 'away' : null); + }) + .catch((error) => { + LoggerProxy.logger.error('Meeting:index#beRightBack --> Error ', error); + + return Promise.reject(error); + }); + } + /** * Remove the member from the meeting, boot them * @param {String} memberId diff --git a/packages/@webex/plugin-meetings/src/meeting/request.ts b/packages/@webex/plugin-meetings/src/meeting/request.ts index 89714139bef..5aa37e76335 100644 --- a/packages/@webex/plugin-meetings/src/meeting/request.ts +++ b/packages/@webex/plugin-meetings/src/meeting/request.ts @@ -28,7 +28,7 @@ import { ANNOTATION, IP_VERSION, } from '../constants'; -import {SendReactionOptions, ToggleReactionsOptions} from './request.type'; +import {SendReactionOptions, BrbOptions, ToggleReactionsOptions} from './request.type'; import MeetingUtil from './util'; import {AnnotationInfo} from '../annotation/annotation.types'; @@ -916,4 +916,29 @@ export default class MeetingRequest extends StatelessWebexPlugin { uri: locusUrl, }); } + + /** + * Sends a request to set be right back status. + * + * @param {Object} options - The options for brb request. + * @param {boolean} options.enabled - Whether brb status is enabled. + * @param {string} options.locusUrl - The URL of the locus. + * @param {string} options.deviceUrl - The URL of the device. + * @param {string} options.selfId - The ID of the participant. + * @returns {Promise} + */ + sendBrb({enabled, locusUrl, deviceUrl, selfId}: BrbOptions) { + const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${CONTROLS}`; + + return this.locusDeltaRequest({ + method: HTTP_VERBS.PATCH, + uri, + body: { + brb: { + enabled, + deviceUrl, + }, + }, + }); + } } diff --git a/packages/@webex/plugin-meetings/src/meeting/request.type.ts b/packages/@webex/plugin-meetings/src/meeting/request.type.ts index bc191f97499..f6bee002cbb 100644 --- a/packages/@webex/plugin-meetings/src/meeting/request.type.ts +++ b/packages/@webex/plugin-meetings/src/meeting/request.type.ts @@ -11,3 +11,10 @@ export type ToggleReactionsOptions = { locusUrl: string; requestingParticipantId: string; }; + +export type BrbOptions = { + enabled: boolean; + locusUrl: string; + deviceUrl: string; + selfId: string; +}; diff --git a/packages/@webex/plugin-meetings/src/multistream/sendSlotManager.ts b/packages/@webex/plugin-meetings/src/multistream/sendSlotManager.ts index 2ed5eacf941..8bc0e3ee7e7 100644 --- a/packages/@webex/plugin-meetings/src/multistream/sendSlotManager.ts +++ b/packages/@webex/plugin-meetings/src/multistream/sendSlotManager.ts @@ -4,6 +4,7 @@ import { LocalStream, MultistreamRoapMediaConnection, NamedMediaGroup, + StreamState, } from '@webex/internal-media-core'; export default class SendSlotManager { @@ -83,6 +84,36 @@ export default class SendSlotManager { ); } + /** + * Sets the source state override for the given media type. + * @param {MediaType} mediaType - The type of media (must be MediaType.VideoMain to apply source state changes). + * @param {StreamState | null} state - The state to set or null to clear the override value. + * @returns {void} + */ + public setSourceStateOverride(mediaType: MediaType, state: StreamState | null) { + if (mediaType !== MediaType.VideoMain) { + throw new Error( + `sendSlotManager cannot set source state override which media type is ${mediaType}` + ); + } + + const slot = this.slots.get(mediaType); + + if (!slot) { + throw new Error(`Slot for ${mediaType} does not exist`); + } + + if (state) { + slot.setSourceStateOverride(state); + } else { + slot.clearSourceStateOverride(); + } + + this.LoggerProxy.logger.info( + `SendSlotsManager->setSourceStateOverride#set source state override for ${mediaType} to ${state}` + ); + } + /** * This method publishes the given stream to the sendSlot for the given mediaType * @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES) diff --git a/packages/@webex/plugin-meetings/test/unit/spec/locus-info/index.js b/packages/@webex/plugin-meetings/test/unit/spec/locus-info/index.js index 359788cccb5..7981973cdb6 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/locus-info/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/locus-info/index.js @@ -738,6 +738,75 @@ describe('plugin-meetings', () => { }); describe('#updateSelf', () => { + it('should trigger SELF_MEETING_BRB_CHANGED when brb state changed', () => { + locusInfo.self = undefined; + + const assertBrb = (enabled) => { + const selfWithBrbChanged = cloneDeep(self); + selfWithBrbChanged.controls.brb = enabled; + + locusInfo.emitScoped = sinon.stub(); + locusInfo.updateSelf(selfWithBrbChanged, []); + + assert.calledWith( + locusInfo.emitScoped, + {file: 'locus-info', function: 'updateSelf'}, + LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, + {brb: enabled} + ); + }; + + assertBrb(true); + assertBrb(false); + }); + + it('should not trigger SELF_MEETING_BRB_CHANGED when brb state did not change', () => { + const assertBrbUnchanged = (value) => { + locusInfo.self = undefined; + + const selfWithBrbChanged = cloneDeep(self); + selfWithBrbChanged.controls.brb = value; + locusInfo.self = selfWithBrbChanged; + + locusInfo.emitScoped = sinon.stub(); + + const newSelf = cloneDeep(self); + newSelf.controls.brb = value; + + locusInfo.updateSelf(newSelf, []); + + assert.neverCalledWith( + locusInfo.emitScoped, + {file: 'locus-info', function: 'updateSelf'}, + LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, + {brb: value} + ); + }; + + assertBrbUnchanged(true); + assertBrbUnchanged(false); + }); + + it('should not trigger SELF_MEETING_BRB_CHANGED when brb state changed to undefined', () => { + const selfWithBrbChanged = cloneDeep(self); + selfWithBrbChanged.controls.brb = false; + locusInfo.self = selfWithBrbChanged; + + locusInfo.emitScoped = sinon.stub(); + + const newSelf = cloneDeep(self); + newSelf.controls.brb = undefined; + + locusInfo.updateSelf(newSelf, []); + + assert.neverCalledWith( + locusInfo.emitScoped, + {file: 'locus-info', function: 'updateSelf'}, + LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, + {brb: undefined} + ); + }); + it('should trigger CONTROLS_MEETING_LAYOUT_UPDATED when the meeting layout controls change', () => { const layoutType = 'EXAMPLE TYPE'; diff --git a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js index 87dee5f9ceb..6f53ec821d6 100644 --- a/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js +++ b/packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js @@ -32,8 +32,8 @@ import { NETWORK_STATUS, ONLINE, OFFLINE, - ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT, -} from '@webex/plugin-meetings/src/constants'; + ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT, HTTP_VERBS, PARTICIPANT, CONTROLS, +} from "@webex/plugin-meetings/src/constants"; import { ConnectionState, MediaConnectionEventNames, @@ -3634,6 +3634,80 @@ describe('plugin-meetings', () => { }); }); + describe(`#beRightBack`, () => { + const fakeMultistreamRoapMediaConnection = { + createSendSlot: sinon.stub().returns({ + setSourceStateOverride: sinon.stub().resolves(), + clearSourceStateOverride: sinon.stub().resolves(), + }), + }; + + beforeEach(() => { + meeting.meetingRequest.sendBrb = sinon.stub().resolves({body: 'test'}); + meeting.mediaProperties.webrtcMediaConnection = {createSendSlot: sinon.stub()}; + meeting.sendSlotManager.createSlot( + fakeMultistreamRoapMediaConnection, + MediaType.VideoMain + ); + + meeting.locusUrl = 'locus url'; + meeting.deviceUrl = 'device url'; + meeting.selfId = 'self id'; + }); + + afterEach(() => { + sinon.restore(); + }); + + it('should have #beRightBack', () => { + assert.exists(meeting.beRightBack); + }); + + describe('when in a multistream meeting', () => { + + beforeEach(() => { + meeting.isMultistream = true; + }); + + it('should enable #beRightBack and return a promise', async () => { + const brbResult = meeting.beRightBack(true); + + await brbResult; + assert.exists(brbResult.then); + assert.calledOnce(meeting.meetingRequest.sendBrb); + }) + + it('should disable #beRightBack and return a promise', async () => { + const brbResult = meeting.beRightBack(false); + + await brbResult; + assert.exists(brbResult.then); + assert.calledOnce(meeting.meetingRequest.sendBrb); + }) + }); + + describe('when in a transcoded meeting', () => { + + beforeEach(() => { + meeting.isMultistream = false; + }); + + it('should ignore enabling #beRightBack', async () => { + meeting.beRightBack(true); + + assert.isRejected((Promise.reject())); + assert.notCalled(meeting.meetingRequest.sendBrb); + }) + + it('should ignore disabling #beRightBack', async () => { + meeting.beRightBack(false); + + assert.isRejected((Promise.reject())); + assert.notCalled(meeting.meetingRequest.sendBrb); + }) + }); + }); + /* This set of tests are like semi-integration tests, they use real MuteState, Media, LocusMediaRequest and Roap classes. They mock the @webex/internal-media-core and sending of /media http requests to Locus. Their main purpose is to test that we send the right http requests to Locus and make right calls @@ -4721,6 +4795,7 @@ describe('plugin-meetings', () => { }); }); + [ {mute: true, title: 'user muting a track before confluence is created'}, {mute: false, title: 'user unmuting a track before confluence is created'}, @@ -8554,6 +8629,7 @@ describe('plugin-meetings', () => { }); }); }); + describe('#setUpLocusInfoSelfListener', () => { it('listens to the self unadmitted guest event', (done) => { meeting.startKeepAlive = sinon.stub(); @@ -8634,6 +8710,26 @@ describe('plugin-meetings', () => { ); }); + it('listens to the brb state changed event', () => { + const assertBrb = (enabled) => { + meeting.locusInfo.emit( + { function: 'test', file: 'test' }, + LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED, + { brb: { enabled } }, + ) + assert.calledWithExactly( + TriggerProxy.trigger, + meeting, + {file: 'meeting/index', function: 'setUpLocusInfoSelfListener'}, + EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE, + { payload: { brb: { enabled } } }, + ); + } + + assertBrb(true); + assertBrb(false); + }) + it('listens to the interpretation changed event', () => { meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub(); diff --git a/packages/calling/package.json b/packages/calling/package.json index b5684f56853..451bb2bd9a3 100644 --- a/packages/calling/package.json +++ b/packages/calling/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@types/platform": "1.3.4", - "@webex/internal-media-core": "2.11.3", + "@webex/internal-media-core": "2.12.0", "@webex/media-helpers": "workspace:*", "async-mutex": "0.4.0", "buffer": "6.0.3", diff --git a/yarn.lock b/yarn.lock index a0fe1d26df7..103f54ffac0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2035,21 +2035,21 @@ __metadata: linkType: hard "@babel/runtime-corejs2@npm:^7.25.0": - version: 7.25.6 - resolution: "@babel/runtime-corejs2@npm:7.25.6" + version: 7.26.0 + resolution: "@babel/runtime-corejs2@npm:7.26.0" dependencies: core-js: ^2.6.12 regenerator-runtime: ^0.14.0 - checksum: afd5406391d7e41ac291ae40c549dc8da734ec8d160b560081b4153b4063409b7402e5c469bcebd0789aaeaca740d3fe088cbe45be3a4c7341e3a2b3f48fdd98 + checksum: f024cc7f5e6429a2d0bc95feff95c9a8bef6fd9ee50dfaefbb28760fa277af223eeb16ec7504ab1511ad24cfda416ade1568c3b16f5b1da06d353915bae78c40 languageName: node linkType: hard -"@babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.25.6": - version: 7.25.6 - resolution: "@babel/runtime@npm:7.25.6" +"@babel/runtime@npm:^7.18.9, @babel/runtime@npm:^7.26.0": + version: 7.26.0 + resolution: "@babel/runtime@npm:7.26.0" dependencies: regenerator-runtime: ^0.14.0 - checksum: ee1a69d3ac7802803f5ee6a96e652b78b8addc28c6a38c725a4ad7d61a059d9e6cb9f6550ed2f63cce67a1bd82e0b1ef66a1079d895be6bfb536a5cfbd9ccc32 + checksum: c8e2c0504ab271b3467a261a8f119bf2603eb857a0d71e37791f4e3fae00f681365073cc79f141ddaa90c6077c60ba56448004ad5429d07ac73532be9f7cf28a languageName: node linkType: hard @@ -5921,11 +5921,11 @@ __metadata: linkType: hard "@types/node@npm:^20.14.1": - version: 20.16.5 - resolution: "@types/node@npm:20.16.5" + version: 20.17.6 + resolution: "@types/node@npm:20.17.6" dependencies: undici-types: ~6.19.2 - checksum: f38b7bd8c4993dcf38943afa2ffdd7dfd18fc94f8f3f28d0c1045a10d39871a6cc1b8f8d3bf0c7ed848457d0e1d283482f6ca125579c13fed1b7575d23e8e8f5 + checksum: d51dbb9881c94d0310b32b5fd8013e3261595c61bc888fa27258469c93c3dc0b3c4d20a9f28f3f5f79562f6737e28e7f3dd04940dc8b4d966d34aaf318f7f69b languageName: node linkType: hard @@ -7419,7 +7419,7 @@ __metadata: "@typescript-eslint/eslint-plugin": 5.38.1 "@typescript-eslint/parser": 5.38.1 "@web/dev-server": 0.4.5 - "@webex/internal-media-core": 2.11.3 + "@webex/internal-media-core": 2.12.0 "@webex/media-helpers": "workspace:*" async-mutex: 0.4.0 buffer: 6.0.3 @@ -7710,22 +7710,22 @@ __metadata: languageName: unknown linkType: soft -"@webex/internal-media-core@npm:2.11.3": - version: 2.11.3 - resolution: "@webex/internal-media-core@npm:2.11.3" +"@webex/internal-media-core@npm:2.12.0": + version: 2.12.0 + resolution: "@webex/internal-media-core@npm:2.12.0" dependencies: "@babel/runtime": ^7.18.9 "@babel/runtime-corejs2": ^7.25.0 "@webex/rtcstats": ^1.5.0 "@webex/ts-sdp": 1.7.0 "@webex/web-capabilities": ^1.4.1 - "@webex/web-client-media-engine": 3.24.2 + "@webex/web-client-media-engine": 3.25.0 events: ^3.3.0 typed-emitter: ^2.1.0 uuid: ^8.3.2 webrtc-adapter: ^8.1.2 xstate: ^4.30.6 - checksum: b95c917890c98ded1346d093656a8c54cb4ae7c0a8a93ccccaf39e72913f3c2c8a53829d257eb5ac74a087ee2c4583c37ed3752acfbab179e6533761eb9a5ed0 + checksum: 9631705b5423684fb01747a8fc979b746727eaa5bf5cbbe122155cc9b5de52c839b1aa1df2152b5f32e2e06ae7e8f8a59a4eb655c9f970d6525d1e8571a31a42 languageName: node linkType: hard @@ -8392,10 +8392,10 @@ __metadata: languageName: unknown linkType: soft -"@webex/json-multistream@npm:2.1.6": - version: 2.1.6 - resolution: "@webex/json-multistream@npm:2.1.6" - checksum: 18cd8e24151c88fc563c6224cc358c9e2e3cda78d80baddba8dd58aa3e79bf4d78ff12613b27cad5a0242856e84e3c6001e12916e404a68398c68e5439e5154b +"@webex/json-multistream@npm:2.2.0": + version: 2.2.0 + resolution: "@webex/json-multistream@npm:2.2.0" + checksum: 2f3f8de556e083cd7e9aa0fa0ddec13c5d0a63f5d809e5ce7075110cfa34178f12b9dde2e093fa49ed6f78fcbbe72de9059d2cbfd23e371e4c595baf9ad0449b languageName: node linkType: hard @@ -8484,7 +8484,7 @@ __metadata: "@babel/preset-typescript": 7.22.11 "@webex/babel-config-legacy": "workspace:*" "@webex/eslint-config-legacy": "workspace:*" - "@webex/internal-media-core": 2.11.3 + "@webex/internal-media-core": 2.12.0 "@webex/jest-config-legacy": "workspace:*" "@webex/legacy-tools": "workspace:*" "@webex/test-helper-chai": "workspace:*" @@ -8720,7 +8720,7 @@ __metadata: "@webex/babel-config-legacy": "workspace:*" "@webex/common": "workspace:*" "@webex/eslint-config-legacy": "workspace:*" - "@webex/internal-media-core": 2.11.3 + "@webex/internal-media-core": 2.12.0 "@webex/internal-plugin-conversation": "workspace:*" "@webex/internal-plugin-device": "workspace:*" "@webex/internal-plugin-llm": "workspace:*" @@ -9423,11 +9423,11 @@ __metadata: languageName: node linkType: hard -"@webex/web-client-media-engine@npm:3.24.2": - version: 3.24.2 - resolution: "@webex/web-client-media-engine@npm:3.24.2" +"@webex/web-client-media-engine@npm:3.25.0": + version: 3.25.0 + resolution: "@webex/web-client-media-engine@npm:3.25.0" dependencies: - "@webex/json-multistream": 2.1.6 + "@webex/json-multistream": 2.2.0 "@webex/rtcstats": ^1.5.0 "@webex/ts-events": ^1.0.1 "@webex/ts-sdp": 1.7.0 @@ -9438,7 +9438,7 @@ __metadata: js-logger: ^1.6.1 typed-emitter: ^2.1.0 uuid: ^8.3.2 - checksum: 26ac75ffcb519a11b3a9f2b601b13b85c4c4e4b685503da0e752d03137af65d22472329f45fbd08de138d93ebd92295645f98c9b879617c838afee61c0589df7 + checksum: 8a64aed477f8d82a4c4de0edff44007110acb1b8333cf5de56a98e8db5b03603c0880caa57ce48a2bf7859eaa41d4f3afdccca57ba85645904f0a02a46c9ea1c languageName: node linkType: hard @@ -16977,13 +16977,13 @@ __metadata: languageName: node linkType: hard -"fast-unique-numbers@npm:^9.0.9": - version: 9.0.9 - resolution: "fast-unique-numbers@npm:9.0.9" +"fast-unique-numbers@npm:^9.0.13": + version: 9.0.13 + resolution: "fast-unique-numbers@npm:9.0.13" dependencies: - "@babel/runtime": ^7.25.6 - tslib: ^2.7.0 - checksum: 58481531260a91d57859a631378609368a5c3828a36da2e833f1b82f205eec7fe698df76151af84a4bbb1bf911ed9659b79e646d418462d7981e0e24e48f6394 + "@babel/runtime": ^7.26.0 + tslib: ^2.8.0 + checksum: 759f9aa612ccfd1c8196b33531d2117542cb79e21c564308331831757f4ff192ccc3baa9d64155bf98217509c66609835cf9809c73f01d5aa70220062e1affda languageName: node linkType: hard @@ -31145,10 +31145,10 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.7.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 1606d5c89f88d466889def78653f3aab0f88692e80bb2066d090ca6112ae250ec1cfa9dbfaab0d17b60da15a4186e8ec4d893801c67896b277c17374e36e1d28 +"tslib@npm:^2.8.0": + version: 2.8.0 + resolution: "tslib@npm:2.8.0" + checksum: de852ecd81adfdb4870927e250763345f07dc13fe7f395ce261424966bb122a0992ad844c3ec875c9e63e72afe2220a150712984e44dfd1a8a7e538a064e3d46 languageName: node linkType: hard @@ -33308,37 +33308,37 @@ __metadata: languageName: node linkType: hard -"worker-timers-broker@npm:^7.1.2": - version: 7.1.2 - resolution: "worker-timers-broker@npm:7.1.2" +"worker-timers-broker@npm:^7.1.6": + version: 7.1.6 + resolution: "worker-timers-broker@npm:7.1.6" dependencies: - "@babel/runtime": ^7.25.6 - fast-unique-numbers: ^9.0.9 - tslib: ^2.7.0 - worker-timers-worker: ^8.0.4 - checksum: ec2deb097662ef2331cdf4681023fe970504cd30b58cbf13ceab0741d05fd21a49e518c73f03b20a01e2684d9b8d6d59787aed82d05e615be99c8dc0229623c4 + "@babel/runtime": ^7.26.0 + fast-unique-numbers: ^9.0.13 + tslib: ^2.8.0 + worker-timers-worker: ^8.0.8 + checksum: 5144367680a0354bab72f0be01f60f122c5a3386b4f412bb48ca045bd3965ed379a1943cfd61797fce56f4b1da8127f9c528ff5d9bd0a2e138936cac7eabe2b0 languageName: node linkType: hard -"worker-timers-worker@npm:^8.0.4": - version: 8.0.4 - resolution: "worker-timers-worker@npm:8.0.4" +"worker-timers-worker@npm:^8.0.8": + version: 8.0.8 + resolution: "worker-timers-worker@npm:8.0.8" dependencies: - "@babel/runtime": ^7.25.6 - tslib: ^2.7.0 - checksum: fd59d4c947895efd036e46cd8d4c288b228256f7bac24ff5b83c682ef44e53584ce8bc4f0525eee469be00dbf1f89e9266682d0297df7478704996087a7553b2 + "@babel/runtime": ^7.26.0 + tslib: ^2.8.0 + checksum: 836e43698032f5132b56b1617ebe5129553f1caf78fca54e3770c6a7a253bff7697764383c8c5736d62ec27019d777c228746743e12507ce8d62eecc165a057b languageName: node linkType: hard "worker-timers@npm:^8.0.2": - version: 8.0.5 - resolution: "worker-timers@npm:8.0.5" - dependencies: - "@babel/runtime": ^7.25.6 - tslib: ^2.7.0 - worker-timers-broker: ^7.1.2 - worker-timers-worker: ^8.0.4 - checksum: e8c00e33e5af252a37472c0fc1bc3c3b26cdd174e179ab1b301364b880db7e560f6f3eb82a4438970fe113c1e9a4855569df0ef61edc6d5de613a51789587ef4 + version: 8.0.10 + resolution: "worker-timers@npm:8.0.10" + dependencies: + "@babel/runtime": ^7.26.0 + tslib: ^2.8.0 + worker-timers-broker: ^7.1.6 + worker-timers-worker: ^8.0.8 + checksum: f64e7d92d226b7f218fd71ab549ece7967a6e35b5966dbdee4cfe359b01c8a0ab1718c396d7947e17a463d0a5a130a80fb49663755c5e17f23c7e21e01dfdfd9 languageName: node linkType: hard