@webex/plugin-meetings 3.0.0-beta.111 → 3.0.0-beta.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/media/index.js +0 -21
  4. package/dist/media/index.js.map +1 -1
  5. package/dist/meeting/index.js +19 -0
  6. package/dist/meeting/index.js.map +1 -1
  7. package/dist/meeting/locusMediaRequest.js +288 -0
  8. package/dist/meeting/locusMediaRequest.js.map +1 -0
  9. package/dist/meeting/muteState.js +49 -34
  10. package/dist/meeting/muteState.js.map +1 -1
  11. package/dist/meeting/request.js +0 -39
  12. package/dist/meeting/request.js.map +1 -1
  13. package/dist/meeting/util.js +17 -18
  14. package/dist/meeting/util.js.map +1 -1
  15. package/dist/roap/index.js +4 -19
  16. package/dist/roap/index.js.map +1 -1
  17. package/dist/roap/request.js +23 -39
  18. package/dist/roap/request.js.map +1 -1
  19. package/dist/roap/turnDiscovery.js +2 -10
  20. package/dist/roap/turnDiscovery.js.map +1 -1
  21. package/dist/types/meeting/index.d.ts +3 -0
  22. package/dist/types/meeting/locusMediaRequest.d.ts +68 -0
  23. package/dist/types/meeting/muteState.d.ts +3 -2
  24. package/dist/types/meeting/request.d.ts +0 -18
  25. package/dist/types/roap/request.d.ts +6 -8
  26. package/dist/types/roap/turnDiscovery.d.ts +4 -1
  27. package/package.json +19 -19
  28. package/src/media/index.ts +0 -23
  29. package/src/meeting/index.ts +22 -0
  30. package/src/meeting/locusMediaRequest.ts +303 -0
  31. package/src/meeting/muteState.ts +24 -9
  32. package/src/meeting/request.ts +0 -46
  33. package/src/meeting/util.ts +15 -13
  34. package/src/roap/index.ts +4 -16
  35. package/src/roap/request.ts +22 -42
  36. package/src/roap/turnDiscovery.ts +2 -8
  37. package/test/unit/spec/meeting/locusMediaRequest.ts +414 -0
  38. package/test/unit/spec/meeting/muteState.js +97 -71
  39. package/test/unit/spec/meeting/utils.js +11 -37
  40. package/test/unit/spec/roap/index.ts +2 -37
  41. package/test/unit/spec/roap/request.ts +27 -57
  42. package/test/unit/spec/roap/turnDiscovery.ts +3 -5
@@ -0,0 +1,303 @@
1
+ /* eslint-disable valid-jsdoc */
2
+ import {defer} from 'lodash';
3
+ import {Defer} from '@webex/common';
4
+ import {WebexPlugin} from '@webex/webex-core';
5
+ import {MEDIA, HTTP_VERBS, ROAP} from '../constants';
6
+ import LoggerProxy from '../common/logs/logger-proxy';
7
+
8
+ export type MediaRequestType = 'RoapMessage' | 'LocalMute';
9
+ export type RequestResult = any;
10
+
11
+ export type RoapRequest = {
12
+ type: 'RoapMessage';
13
+ selfUrl: string;
14
+ mediaId: string;
15
+ roapMessage: any;
16
+ reachability: any;
17
+ joinCookie: any; // any, because this is opaque to the client, we pass whatever object we got from one backend component (Orpheus) to the other (Locus)
18
+ };
19
+
20
+ export type LocalMuteRequest = {
21
+ type: 'LocalMute';
22
+ selfUrl: string;
23
+ mediaId: string;
24
+ muteOptions: {
25
+ audioMuted?: boolean;
26
+ videoMuted?: boolean;
27
+ };
28
+ };
29
+
30
+ export type Request = RoapRequest | LocalMuteRequest;
31
+
32
+ /** Class representing a single /media request being sent to Locus */
33
+ class InternalRequestInfo {
34
+ public readonly request: Request;
35
+ private pendingPromises: Defer[];
36
+ private sendRequestFn: (request: Request) => Promise<RequestResult>;
37
+
38
+ /** Constructor */
39
+ constructor(
40
+ request: Request,
41
+ pendingPromise: Defer,
42
+ sendRequestFn: (request: Request) => Promise<RequestResult>
43
+ ) {
44
+ this.request = request;
45
+ this.pendingPromises = [pendingPromise];
46
+ this.sendRequestFn = sendRequestFn;
47
+ }
48
+
49
+ /**
50
+ * Returns the list of pending promises associated with this request
51
+ */
52
+ public getPendingPromises() {
53
+ return this.pendingPromises;
54
+ }
55
+
56
+ /**
57
+ * Adds promises to the list of pending promises associated with this request
58
+ */
59
+ public addPendingPromises(pendingPromises: Defer[]) {
60
+ this.pendingPromises.push(...pendingPromises);
61
+ }
62
+
63
+ /**
64
+ * Executes the request. Returned promise is resolved once the request
65
+ * is completed (no matter if it succeeded or failed).
66
+ */
67
+ public execute(): Promise<void> {
68
+ return this.sendRequestFn(this.request)
69
+ .then((result) => {
70
+ // resolve all the pending promises associated with this request
71
+ this.pendingPromises.forEach((d) => d.resolve(result));
72
+ })
73
+ .catch((e) => {
74
+ // reject all the pending promises associated with this request
75
+ this.pendingPromises.forEach((d) => d.reject(e));
76
+ });
77
+ }
78
+ }
79
+
80
+ export type Config = {
81
+ device: {
82
+ url: string;
83
+ deviceType: string;
84
+ };
85
+ correlationId: string;
86
+ preferTranscoding: boolean;
87
+ };
88
+
89
+ /**
90
+ * Returns true if the request is triggering confluence creation in the server
91
+ */
92
+ function isRequestAffectingConfluenceState(request: Request): boolean {
93
+ return (
94
+ request.type === 'RoapMessage' && request.roapMessage.messageType === ROAP.ROAP_TYPES.OFFER
95
+ );
96
+ }
97
+
98
+ /**
99
+ * This class manages all /media API requests to Locus. Every call to that
100
+ * Locus API has to go through this class.
101
+ */
102
+ export class LocusMediaRequest extends WebexPlugin {
103
+ private config: Config;
104
+ private latestAudioMuted?: boolean;
105
+ private latestVideoMuted?: boolean;
106
+ private isRequestInProgress: boolean;
107
+ private queuedRequests: InternalRequestInfo[];
108
+ private confluenceState: 'not created' | 'creation in progress' | 'created';
109
+ /**
110
+ * Constructor
111
+ */
112
+ constructor(config: Config, options: any) {
113
+ super({}, options);
114
+ this.isRequestInProgress = false;
115
+ this.queuedRequests = [];
116
+ this.config = config;
117
+ this.confluenceState = 'not created';
118
+ }
119
+
120
+ /**
121
+ * Add a request to the internal queue.
122
+ */
123
+ private addToQueue(info: InternalRequestInfo) {
124
+ if (info.request.type === 'LocalMute' && this.queuedRequests.length > 0) {
125
+ // We don't need additional local mute requests in the queue.
126
+ // We only need at most 1 local mute or 1 roap request, because
127
+ // roap requests also include mute state, so whatever request
128
+ // is sent out, it will send the latest local mute state.
129
+ // We only need to store the pendingPromises so that they get resolved
130
+ // when the roap request is sent out.
131
+ this.queuedRequests[0].addPendingPromises(info.getPendingPromises());
132
+
133
+ return;
134
+ }
135
+
136
+ if (info.request.type === 'RoapMessage' && this.queuedRequests.length > 0) {
137
+ // remove any LocalMute requests from the queue, because this Roap message
138
+ // will also update the mute status in Locus, so they are redundant
139
+ this.queuedRequests = this.queuedRequests.filter((r) => {
140
+ if (r.request.type === 'LocalMute') {
141
+ // we need to keep the pending promises from the local mute request
142
+ // that we're removing from the queue
143
+ info.addPendingPromises(r.getPendingPromises());
144
+
145
+ return false;
146
+ }
147
+
148
+ return true;
149
+ });
150
+ }
151
+
152
+ this.queuedRequests.push(info);
153
+ }
154
+
155
+ /**
156
+ * Takes the next request from the queue and executes it. Once that
157
+ * request is completed, the next one will be taken from the queue
158
+ * and executed and this is repeated until the queue is empty.
159
+ */
160
+ private executeNextQueuedRequest(): void {
161
+ if (this.isRequestInProgress) {
162
+ return;
163
+ }
164
+
165
+ const nextRequest = this.queuedRequests.shift();
166
+
167
+ if (nextRequest) {
168
+ this.isRequestInProgress = true;
169
+ nextRequest.execute().then(() => {
170
+ this.isRequestInProgress = false;
171
+ this.executeNextQueuedRequest();
172
+ });
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Returns latest requested audio and video mute values. If they have never been
178
+ * requested, we assume audio/video to be muted.
179
+ */
180
+ private getLatestMuteState() {
181
+ const audioMuted = this.latestAudioMuted !== undefined ? this.latestAudioMuted : true;
182
+ const videoMuted = this.latestVideoMuted !== undefined ? this.latestVideoMuted : true;
183
+
184
+ return {audioMuted, videoMuted};
185
+ }
186
+
187
+ /**
188
+ * Prepares the uri and body for the media request to be sent to Locus
189
+ */
190
+ private sendHttpRequest(request: Request) {
191
+ const uri = `${request.selfUrl}/${MEDIA}`;
192
+
193
+ const {audioMuted, videoMuted} = this.getLatestMuteState();
194
+
195
+ // first setup things common to all requests
196
+ const body: any = {
197
+ device: this.config.device,
198
+ correlationId: this.config.correlationId,
199
+ clientMediaPreferences: {
200
+ preferTranscoding: this.config.preferTranscoding,
201
+ },
202
+ };
203
+
204
+ const localMedias: any = {
205
+ audioMuted,
206
+ videoMuted,
207
+ };
208
+
209
+ // now add things specific to request type
210
+ switch (request.type) {
211
+ case 'LocalMute':
212
+ body.respOnlySdp = true;
213
+ body.usingResource = null;
214
+ break;
215
+
216
+ case 'RoapMessage':
217
+ localMedias.roapMessage = request.roapMessage;
218
+ localMedias.reachability = request.reachability;
219
+ body.clientMediaPreferences.joinCookie = request.joinCookie;
220
+ break;
221
+ }
222
+
223
+ body.localMedias = [
224
+ {
225
+ localSdp: JSON.stringify(localMedias), // this part must be JSON stringified, Locus requires this
226
+ mediaId: request.mediaId,
227
+ },
228
+ ];
229
+
230
+ LoggerProxy.logger.info(
231
+ `Meeting:LocusMediaRequest#sendHttpRequest --> ${request.type} audioMuted=${audioMuted} videoMuted=${videoMuted}`
232
+ );
233
+
234
+ if (isRequestAffectingConfluenceState(request) && this.confluenceState === 'not created') {
235
+ this.confluenceState = 'creation in progress';
236
+ }
237
+
238
+ // @ts-ignore
239
+ return this.request({
240
+ method: HTTP_VERBS.PUT,
241
+ uri,
242
+ body,
243
+ })
244
+ .then((result) => {
245
+ if (isRequestAffectingConfluenceState(request)) {
246
+ this.confluenceState = 'created';
247
+ }
248
+
249
+ return result;
250
+ })
251
+ .catch((e) => {
252
+ if (
253
+ isRequestAffectingConfluenceState(request) &&
254
+ this.confluenceState === 'creation in progress'
255
+ ) {
256
+ this.confluenceState = 'not created';
257
+ }
258
+ throw e;
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Sends a media request to Locus
264
+ */
265
+ public send(request: Request): Promise<RequestResult> {
266
+ if (request.type === 'LocalMute') {
267
+ const {audioMuted, videoMuted} = request.muteOptions;
268
+
269
+ if (audioMuted !== undefined) {
270
+ this.latestAudioMuted = audioMuted;
271
+ }
272
+ if (videoMuted !== undefined) {
273
+ this.latestVideoMuted = videoMuted;
274
+ }
275
+
276
+ if (this.confluenceState === 'not created') {
277
+ // if there is no confluence, there is no point sending out local mute request
278
+ // as it will fail so we just store the latest audio/video muted values
279
+ // and resolve immediately, so that higher layer (MuteState class) doesn't get blocked
280
+ // and can call us again if user mutes/unmutes again before confluence is created
281
+ LoggerProxy.logger.info(
282
+ 'Meeting:LocusMediaRequest#send --> called with LocalMute request before confluence creation'
283
+ );
284
+
285
+ return Promise.resolve({});
286
+ }
287
+ }
288
+
289
+ const pendingPromise = new Defer();
290
+
291
+ const newRequest = new InternalRequestInfo(
292
+ request,
293
+ pendingPromise,
294
+ this.sendHttpRequest.bind(this)
295
+ );
296
+
297
+ this.addToQueue(newRequest);
298
+
299
+ defer(() => this.executeNextQueuedRequest());
300
+
301
+ return pendingPromise.promise;
302
+ }
303
+ }
@@ -34,8 +34,10 @@ export const createMuteState = (type, meeting, mediaDirection, sdkOwnsLocalTrack
34
34
  the last requested state by the client.
35
35
 
36
36
  More info about Locus muting API: https://sqbu-github.cisco.com/pages/WebExSquared/locus/guides/mute.html#
37
+
38
+ This class is exported only for unit tests. It should never be instantiated directly with new MuteState(), instead createMuteState() should be called
37
39
  */
38
- class MuteState {
40
+ export class MuteState {
39
41
  pendingPromiseReject: any;
40
42
  pendingPromiseResolve: any;
41
43
  state: any;
@@ -62,7 +64,7 @@ class MuteState {
62
64
  localMute: false,
63
65
  },
64
66
  server: {
65
- localMute: false,
67
+ localMute: true,
66
68
  // because remoteVideoMuted and unmuteVideoAllowed are updated seperately, they might be undefined
67
69
  remoteMute: type === AUDIO ? meeting.remoteMuted : meeting.remoteVideoMuted ?? false,
68
70
  unmuteAllowed: type === AUDIO ? meeting.unmuteAllowed : meeting.unmuteVideoAllowed ?? true,
@@ -95,7 +97,7 @@ class MuteState {
95
97
  : meeting.mediaProperties.videoTrack?.muted;
96
98
 
97
99
  LoggerProxy.logger.info(
98
- `Meeting:muteState#start --> ${this.type}: local track initial mute state: ${initialMute}`
100
+ `Meeting:muteState#init --> ${this.type}: local track initial mute state: ${initialMute}`
99
101
  );
100
102
 
101
103
  if (initialMute !== undefined) {
@@ -103,6 +105,19 @@ class MuteState {
103
105
 
104
106
  this.applyClientStateToServer(meeting);
105
107
  }
108
+ } else {
109
+ // in the mode where sdkOwnsLocalTrack is false (transcoded meetings),
110
+ // SDK API currently doesn't allow to start with audio/video muted,
111
+ // so we need to apply the initial local mute state (false) to server
112
+ this.state.syncToServerInProgress = true;
113
+ this.sendLocalMuteRequestToServer(meeting)
114
+ .then(() => {
115
+ this.state.syncToServerInProgress = false;
116
+ })
117
+ .catch(() => {
118
+ this.state.syncToServerInProgress = false;
119
+ // not much we can do here...
120
+ });
106
121
  }
107
122
  }
108
123
 
@@ -312,16 +327,14 @@ class MuteState {
312
327
  * @returns {Promise}
313
328
  */
314
329
  private sendLocalMuteRequestToServer(meeting?: any) {
315
- const audioMuted =
316
- this.type === AUDIO ? this.state.client.localMute : meeting.audio?.state.client.localMute;
317
- const videoMuted =
318
- this.type === VIDEO ? this.state.client.localMute : meeting.video?.state.client.localMute;
330
+ const audioMuted = this.type === AUDIO ? this.state.client.localMute : undefined;
331
+ const videoMuted = this.type === VIDEO ? this.state.client.localMute : undefined;
319
332
 
320
333
  LoggerProxy.logger.info(
321
334
  `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: sending local mute (audio=${audioMuted}, video=${videoMuted}) to server`
322
335
  );
323
336
 
324
- return MeetingUtil.remoteUpdateAudioVideo(audioMuted, videoMuted, meeting)
337
+ return MeetingUtil.remoteUpdateAudioVideo(meeting, audioMuted, videoMuted)
325
338
  .then((locus) => {
326
339
  LoggerProxy.logger.info(
327
340
  `Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`
@@ -329,7 +342,9 @@ class MuteState {
329
342
 
330
343
  this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;
331
344
 
332
- meeting.locusInfo.onFullLocus(locus);
345
+ if (locus) {
346
+ meeting.locusInfo.onFullLocus(locus);
347
+ }
333
348
 
334
349
  return locus;
335
350
  })
@@ -593,52 +593,6 @@ export default class MeetingRequest extends StatelessWebexPlugin {
593
593
  });
594
594
  }
595
595
 
596
- /**
597
- * Toggle remote audio and/or video
598
- * @param {Object} options options for toggling
599
- * @param {String} options.selfId Locus self id??
600
- * @param {String} options.locusUrl Locus url
601
- * @param {String} options.deviceUrl Url of a device
602
- * @param {String} options.resourceId Populated if you are paired to a device
603
- * @param {String} options.localMedias local sdps
604
- * @param {Boolean} options.preferTranscoding false for multistream (Homer), true for transcoded media (Edonus)
605
- * @returns {Promise}
606
- */
607
- remoteAudioVideoToggle(
608
- options:
609
- | {
610
- selfId: string;
611
- locusUrl: string;
612
- deviceUrl: string;
613
- resourceId: string;
614
- localMedias: string;
615
- }
616
- | any
617
- ) {
618
- const uri = `${options.locusUrl}/${PARTICIPANT}/${options.selfId}/${MEDIA}`;
619
- const body = {
620
- device: {
621
- // @ts-ignore
622
- deviceType: this.config.meetings.deviceType,
623
- url: options.deviceUrl,
624
- },
625
- usingResource: options.resourceId || null,
626
- correlationId: options.correlationId,
627
- respOnlySdp: true,
628
- localMedias: options.localMedias,
629
- clientMediaPreferences: {
630
- preferTranscoding: options.preferTranscoding ?? true,
631
- },
632
- };
633
-
634
- // @ts-ignore
635
- return this.request({
636
- method: HTTP_VERBS.PUT,
637
- uri,
638
- body,
639
- });
640
- }
641
-
642
596
  /**
643
597
  * change the content floor grant
644
598
  * @param {Object} options options for floor grant
@@ -44,33 +44,35 @@ MeetingUtil.parseLocusJoin = (response) => {
44
44
  return parsed;
45
45
  };
46
46
 
47
- MeetingUtil.remoteUpdateAudioVideo = (audioMuted, videoMuted, meeting) => {
47
+ MeetingUtil.remoteUpdateAudioVideo = (meeting, audioMuted?: boolean, videoMuted?: boolean) => {
48
48
  if (!meeting) {
49
49
  return Promise.reject(new ParameterError('You need a meeting object.'));
50
50
  }
51
- const localMedias = Media.generateLocalMedias(meeting.mediaId, audioMuted, videoMuted);
52
51
 
53
- if (isEmpty(localMedias)) {
52
+ if (!meeting.locusMediaRequest) {
54
53
  return Promise.reject(
55
- new ParameterError('You need a media id on the meeting to change remote audio.')
54
+ new ParameterError(
55
+ 'You need a meeting with a media connection, call Meeting.addMedia() first.'
56
+ )
56
57
  );
57
58
  }
58
59
 
59
60
  Metrics.postEvent({event: eventType.MEDIA_REQUEST, meeting});
60
61
 
61
- return meeting.meetingRequest
62
- .remoteAudioVideoToggle({
63
- locusUrl: meeting.locusUrl,
64
- selfId: meeting.selfId,
65
- localMedias,
66
- deviceUrl: meeting.deviceUrl,
67
- correlationId: meeting.correlationId,
68
- preferTranscoding: !meeting.isMultistream,
62
+ return meeting.locusMediaRequest
63
+ .send({
64
+ type: 'LocalMute',
65
+ selfUrl: meeting.selfUrl,
66
+ mediaId: meeting.mediaId,
67
+ muteOptions: {
68
+ audioMuted,
69
+ videoMuted,
70
+ },
69
71
  })
70
72
  .then((response) => {
71
73
  Metrics.postEvent({event: eventType.MEDIA_RESPONSE, meeting});
72
74
 
73
- return response.body.locus;
75
+ return response?.body?.locus;
74
76
  });
75
77
  };
76
78
 
package/src/roap/index.ts CHANGED
@@ -98,11 +98,8 @@ export default class Roap extends StatelessWebexPlugin {
98
98
  roapMessage,
99
99
  locusSelfUrl: meeting.selfUrl,
100
100
  mediaId: options.mediaId,
101
- correlationId: options.correlationId,
102
- audioMuted: meeting.audio?.isLocallyMuted(),
103
- videoMuted: meeting.video?.isLocallyMuted(),
104
101
  meetingId: meeting.id,
105
- preferTranscoding: !meeting.isMultistream,
102
+ locusMediaRequest: meeting.locusMediaRequest,
106
103
  })
107
104
  .then(() => {
108
105
  LoggerProxy.logger.log(`Roap:index#sendRoapOK --> ROAP OK sent with seq ${options.seq}`);
@@ -135,11 +132,8 @@ export default class Roap extends StatelessWebexPlugin {
135
132
  roapMessage,
136
133
  locusSelfUrl: meeting.selfUrl,
137
134
  mediaId: options.mediaId,
138
- correlationId: options.correlationId,
139
- audioMuted: meeting.isAudioMuted(),
140
- videoMuted: meeting.isVideoMuted(),
141
135
  meetingId: meeting.id,
142
- preferTranscoding: !meeting.isMultistream,
136
+ locusMediaRequest: meeting.locusMediaRequest,
143
137
  });
144
138
  }
145
139
 
@@ -167,11 +161,8 @@ export default class Roap extends StatelessWebexPlugin {
167
161
  roapMessage,
168
162
  locusSelfUrl: meeting.selfUrl,
169
163
  mediaId: options.mediaId,
170
- correlationId: options.correlationId,
171
- audioMuted: meeting.audio?.isLocallyMuted(),
172
- videoMuted: meeting.video?.isLocallyMuted(),
173
164
  meetingId: meeting.id,
174
- preferTranscoding: !meeting.isMultistream,
165
+ locusMediaRequest: meeting.locusMediaRequest,
175
166
  })
176
167
  .then(() => {
177
168
  LoggerProxy.logger.log(
@@ -206,13 +197,10 @@ export default class Roap extends StatelessWebexPlugin {
206
197
 
207
198
  return this.roapRequest.sendRoap({
208
199
  roapMessage,
209
- correlationId: meeting.correlationId,
210
200
  locusSelfUrl: meeting.selfUrl,
211
201
  mediaId: sendEmptyMediaId ? '' : meeting.mediaId,
212
- audioMuted: meeting.audio?.isLocallyMuted(),
213
- videoMuted: meeting.video?.isLocallyMuted(),
214
202
  meetingId: meeting.id,
215
- preferTranscoding: !meeting.isMultistream,
203
+ locusMediaRequest: meeting.locusMediaRequest,
216
204
  });
217
205
  })
218
206
 
@@ -3,9 +3,10 @@
3
3
  import {StatelessWebexPlugin} from '@webex/webex-core';
4
4
 
5
5
  import LoggerProxy from '../common/logs/logger-proxy';
6
- import {MEDIA, HTTP_VERBS, REACHABILITY} from '../constants';
6
+ import {REACHABILITY} from '../constants';
7
7
  import Metrics from '../metrics';
8
8
  import {eventType} from '../metrics/config';
9
+ import {LocusMediaRequest} from '../meeting/locusMediaRequest';
9
10
 
10
11
  /**
11
12
  * @class RoapRequest
@@ -64,69 +65,48 @@ export default class RoapRequest extends StatelessWebexPlugin {
64
65
  * @param {String} options.locusSelfUrl
65
66
  * @param {String} options.mediaId
66
67
  * @param {String} options.correlationId
67
- * @param {Boolean} options.audioMuted
68
- * @param {Boolean} options.videoMuted
69
68
  * @param {String} options.meetingId
70
- * @param {Boolean} options.preferTranscoding
71
69
  * @returns {Promise} returns the response/failure of the request
72
70
  */
73
71
  async sendRoap(options: {
74
72
  roapMessage: any;
75
73
  locusSelfUrl: string;
76
74
  mediaId: string;
77
- correlationId: string;
78
- audioMuted: boolean;
79
- videoMuted: boolean;
80
75
  meetingId: string;
81
- preferTranscoding?: boolean;
76
+ locusMediaRequest?: LocusMediaRequest;
82
77
  }) {
83
- const {roapMessage, locusSelfUrl, mediaId, correlationId, meetingId} = options;
78
+ const {roapMessage, locusSelfUrl, mediaId, meetingId, locusMediaRequest} = options;
84
79
 
85
80
  if (!mediaId) {
86
- LoggerProxy.logger.info('Roap:request#sendRoap --> Race Condition /call mediaID not present');
81
+ LoggerProxy.logger.info('Roap:request#sendRoap --> sending empty mediaID');
87
82
  }
88
83
 
84
+ if (!locusMediaRequest) {
85
+ LoggerProxy.logger.warn(
86
+ 'Roap:request#sendRoap --> locusMediaRequest unavailable, not sending roap'
87
+ );
88
+
89
+ return Promise.reject(new Error('sendRoap called when locusMediaRequest is undefined'));
90
+ }
89
91
  const {localSdp: localSdpWithReachabilityData, joinCookie} = await this.attachReachabilityData({
90
92
  roapMessage,
91
- // eslint-disable-next-line no-warning-comments
92
- // TODO: check whats the need for video and audiomute
93
- audioMuted: !!options.audioMuted,
94
- videoMuted: !!options.videoMuted,
95
93
  });
96
94
 
97
- const mediaUrl = `${locusSelfUrl}/${MEDIA}`;
98
- // @ts-ignore
99
- const deviceUrl = this.webex.internal.device.url;
100
-
101
95
  LoggerProxy.logger.info(
102
- `Roap:request#sendRoap --> ${mediaUrl} \n ${roapMessage.messageType} \n seq:${roapMessage.seq}`
96
+ `Roap:request#sendRoap --> ${locusSelfUrl} \n ${roapMessage.messageType} \n seq:${roapMessage.seq}`
103
97
  );
104
98
 
105
99
  Metrics.postEvent({event: eventType.MEDIA_REQUEST, meetingId});
106
100
 
107
- // @ts-ignore
108
- return this.request({
109
- uri: mediaUrl,
110
- method: HTTP_VERBS.PUT,
111
- body: {
112
- device: {
113
- url: deviceUrl,
114
- // @ts-ignore
115
- deviceType: this.config.meetings.deviceType,
116
- },
117
- correlationId,
118
- localMedias: [
119
- {
120
- localSdp: JSON.stringify(localSdpWithReachabilityData),
121
- mediaId: options.mediaId,
122
- },
123
- ],
124
- clientMediaPreferences: {
125
- preferTranscoding: options.preferTranscoding ?? true,
126
- joinCookie,
127
- },
128
- },
129
- })
101
+ return locusMediaRequest
102
+ .send({
103
+ type: 'RoapMessage',
104
+ selfUrl: locusSelfUrl,
105
+ joinCookie,
106
+ mediaId,
107
+ roapMessage,
108
+ reachability: localSdpWithReachabilityData.reachability,
109
+ })
130
110
  .then((res) => {
131
111
  Metrics.postEvent({event: eventType.MEDIA_RESPONSE, meetingId});
132
112
 
@@ -176,15 +176,12 @@ export default class TurnDiscovery {
176
176
  return this.roapRequest
177
177
  .sendRoap({
178
178
  roapMessage,
179
- correlationId: meeting.correlationId,
180
179
  // @ts-ignore - Fix missing type
181
180
  locusSelfUrl: meeting.selfUrl,
182
181
  // @ts-ignore - Fix missing type
183
182
  mediaId: isReconnecting ? '' : meeting.mediaId,
184
- audioMuted: meeting.audio?.isLocallyMuted(),
185
- videoMuted: meeting.video?.isLocallyMuted(),
186
183
  meetingId: meeting.id,
187
- preferTranscoding: !meeting.isMultistream,
184
+ locusMediaRequest: meeting.locusMediaRequest,
188
185
  })
189
186
  .then(({mediaConnections}) => {
190
187
  if (mediaConnections) {
@@ -213,11 +210,8 @@ export default class TurnDiscovery {
213
210
  locusSelfUrl: meeting.selfUrl,
214
211
  // @ts-ignore - fix type
215
212
  mediaId: meeting.mediaId,
216
- correlationId: meeting.correlationId,
217
- audioMuted: meeting.audio?.isLocallyMuted(),
218
- videoMuted: meeting.video?.isLocallyMuted(),
219
213
  meetingId: meeting.id,
220
- preferTranscoding: !meeting.isMultistream,
214
+ locusMediaRequest: meeting.locusMediaRequest,
221
215
  });
222
216
  }
223
217