livekit-server-sdk 2.2.0 → 2.4.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.
Files changed (67) hide show
  1. package/README.md +80 -95
  2. package/dist/AccessToken.d.ts +1 -0
  3. package/dist/AccessToken.d.ts.map +1 -0
  4. package/dist/AccessToken.js +3 -0
  5. package/dist/AccessToken.js.map +1 -1
  6. package/dist/EgressClient.d.ts +1 -0
  7. package/dist/EgressClient.d.ts.map +1 -0
  8. package/dist/EgressClient.js +4 -1
  9. package/dist/EgressClient.js.map +1 -1
  10. package/dist/IngressClient.d.ts +1 -0
  11. package/dist/IngressClient.d.ts.map +1 -0
  12. package/dist/IngressClient.js +3 -0
  13. package/dist/IngressClient.js.map +1 -1
  14. package/dist/RoomServiceClient.d.ts +1 -0
  15. package/dist/RoomServiceClient.d.ts.map +1 -0
  16. package/dist/RoomServiceClient.js.map +1 -1
  17. package/dist/ServiceBase.d.ts +1 -0
  18. package/dist/ServiceBase.d.ts.map +1 -0
  19. package/dist/ServiceBase.js +3 -0
  20. package/dist/ServiceBase.js.map +1 -1
  21. package/dist/SipClient.d.ts +76 -0
  22. package/dist/SipClient.d.ts.map +1 -0
  23. package/dist/SipClient.js +168 -0
  24. package/dist/SipClient.js.map +1 -0
  25. package/dist/TwirpRPC.d.ts +1 -0
  26. package/dist/TwirpRPC.d.ts.map +1 -0
  27. package/dist/TwirpRPC.js.map +1 -1
  28. package/dist/WebhookReceiver.d.ts +2 -1
  29. package/dist/WebhookReceiver.d.ts.map +1 -0
  30. package/dist/WebhookReceiver.js.map +1 -1
  31. package/dist/grants.d.ts +2 -1
  32. package/dist/grants.d.ts.map +1 -0
  33. package/dist/grants.js +3 -0
  34. package/dist/grants.js.map +1 -1
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +5 -1
  38. package/dist/index.js.map +1 -1
  39. package/package.json +5 -1
  40. package/src/AccessToken.test.ts +96 -0
  41. package/src/AccessToken.ts +151 -0
  42. package/src/EgressClient.ts +617 -0
  43. package/src/IngressClient.ts +257 -0
  44. package/src/RoomServiceClient.ts +358 -0
  45. package/src/ServiceBase.ts +35 -0
  46. package/src/SipClient.ts +277 -0
  47. package/src/TwirpRPC.ts +54 -0
  48. package/src/WebhookReceiver.test.ts +44 -0
  49. package/src/WebhookReceiver.ts +85 -0
  50. package/src/grants.test.ts +30 -0
  51. package/src/grants.ts +96 -0
  52. package/src/index.ts +53 -0
  53. package/.changeset/README.md +0 -8
  54. package/.changeset/config.json +0 -11
  55. package/.eslintrc.cjs +0 -17
  56. package/.github/banner_dark.png +0 -0
  57. package/.github/banner_light.png +0 -0
  58. package/.github/workflows/release.yaml +0 -46
  59. package/.github/workflows/test.yaml +0 -46
  60. package/.gitmodules +0 -3
  61. package/.prettierignore +0 -8
  62. package/CHANGELOG.md +0 -81
  63. package/LICENSE +0 -201
  64. package/NOTICE +0 -13
  65. package/renovate.json +0 -18
  66. package/tsconfig.eslint.json +0 -5
  67. package/vite.config.js +0 -8
@@ -0,0 +1,617 @@
1
+ // SPDX-FileCopyrightText: 2024 LiveKit, Inc.
2
+ //
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ import {
5
+ DirectFileOutput,
6
+ EgressInfo,
7
+ EncodedFileOutput,
8
+ EncodingOptions,
9
+ EncodingOptionsPreset,
10
+ ImageOutput,
11
+ ListEgressRequest,
12
+ ListEgressResponse,
13
+ ParticipantEgressRequest,
14
+ RoomCompositeEgressRequest,
15
+ SegmentedFileOutput,
16
+ StopEgressRequest,
17
+ StreamOutput,
18
+ TrackCompositeEgressRequest,
19
+ TrackEgressRequest,
20
+ UpdateLayoutRequest,
21
+ UpdateStreamRequest,
22
+ WebEgressRequest,
23
+ } from '@livekit/protocol';
24
+ import ServiceBase from './ServiceBase.js';
25
+ import { Rpc, TwirpRpc, livekitPackage } from './TwirpRPC.js';
26
+
27
+ const svc = 'Egress';
28
+
29
+ export interface RoomCompositeOptions {
30
+ /**
31
+ * egress layout. optional
32
+ */
33
+ layout?: string;
34
+ /**
35
+ * encoding options or preset. optional
36
+ */
37
+ encodingOptions?: EncodingOptionsPreset | EncodingOptions;
38
+ /**
39
+ * record audio only. optional
40
+ */
41
+ audioOnly?: boolean;
42
+ /**
43
+ * record video only. optional
44
+ */
45
+ videoOnly?: boolean;
46
+ /**
47
+ * custom template url. optional
48
+ */
49
+ customBaseUrl?: string;
50
+ }
51
+
52
+ export interface WebOptions {
53
+ /**
54
+ * encoding options or preset. optional
55
+ */
56
+ encodingOptions?: EncodingOptionsPreset | EncodingOptions;
57
+ /**
58
+ * record audio only. optional
59
+ */
60
+ audioOnly?: boolean;
61
+ /**
62
+ * record video only. optional
63
+ */
64
+ videoOnly?: boolean;
65
+ /**
66
+ * await START_RECORDING chrome log
67
+ */
68
+ awaitStartSignal?: boolean;
69
+ }
70
+
71
+ export interface ParticipantEgressOptions {
72
+ /**
73
+ * true to capture source screenshare and screenshare_audio
74
+ * false to capture camera and microphone
75
+ */
76
+ screenShare?: boolean;
77
+ /**
78
+ * encoding options or preset. optional
79
+ */
80
+ encodingOptions?: EncodingOptionsPreset | EncodingOptions;
81
+ }
82
+
83
+ export interface TrackCompositeOptions {
84
+ /**
85
+ * audio track ID
86
+ */
87
+ audioTrackId?: string;
88
+ /**
89
+ * video track ID
90
+ */
91
+ videoTrackId?: string;
92
+ /**
93
+ * encoding options or preset. optional
94
+ */
95
+ encodingOptions?: EncodingOptionsPreset | EncodingOptions;
96
+ }
97
+
98
+ /**
99
+ * Used to supply multiple outputs with an egress request
100
+ */
101
+ export interface EncodedOutputs {
102
+ file?: EncodedFileOutput | undefined;
103
+ stream?: StreamOutput | undefined;
104
+ segments?: SegmentedFileOutput | undefined;
105
+ images?: ImageOutput | undefined;
106
+ }
107
+
108
+ export interface ListEgressOptions {
109
+ roomName?: string;
110
+ egressId?: string;
111
+ active?: boolean;
112
+ }
113
+
114
+ /**
115
+ * Client to access Egress APIs
116
+ */
117
+ export class EgressClient extends ServiceBase {
118
+ private readonly rpc: Rpc;
119
+
120
+ /**
121
+ * @param host hostname including protocol. i.e. 'https://cluster.livekit.io'
122
+ * @param apiKey API Key, can be set in env var LIVEKIT_API_KEY
123
+ * @param secret API Secret, can be set in env var LIVEKIT_API_SECRET
124
+ */
125
+ constructor(host: string, apiKey?: string, secret?: string) {
126
+ super(apiKey, secret);
127
+ this.rpc = new TwirpRpc(host, livekitPackage);
128
+ }
129
+
130
+ /**
131
+ * @param roomName room name
132
+ * @param output file or stream output
133
+ * @param opts RoomCompositeOptions
134
+ */
135
+ async startRoomCompositeEgress(
136
+ roomName: string,
137
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
138
+ opts?: RoomCompositeOptions,
139
+ ): Promise<EgressInfo>;
140
+ /**
141
+ * @deprecated use RoomCompositeOptions instead
142
+ */
143
+ async startRoomCompositeEgress(
144
+ roomName: string,
145
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
146
+ layout?: string,
147
+ options?: EncodingOptionsPreset | EncodingOptions,
148
+ audioOnly?: boolean,
149
+ videoOnly?: boolean,
150
+ customBaseUrl?: string,
151
+ ): Promise<EgressInfo>;
152
+ async startRoomCompositeEgress(
153
+ roomName: string,
154
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
155
+ optsOrLayout?: RoomCompositeOptions | string,
156
+ options?: EncodingOptionsPreset | EncodingOptions,
157
+ audioOnly?: boolean,
158
+ videoOnly?: boolean,
159
+ customBaseUrl?: string,
160
+ ): Promise<EgressInfo> {
161
+ let layout: string | undefined;
162
+ if (optsOrLayout !== undefined) {
163
+ if (typeof optsOrLayout === 'string') {
164
+ layout = optsOrLayout;
165
+ } else {
166
+ const opts = <RoomCompositeOptions>optsOrLayout;
167
+ layout = opts.layout;
168
+ options = opts.encodingOptions;
169
+ audioOnly = opts.audioOnly;
170
+ videoOnly = opts.videoOnly;
171
+ customBaseUrl = opts.customBaseUrl;
172
+ }
173
+ }
174
+
175
+ layout ??= '';
176
+ audioOnly ??= false;
177
+ videoOnly ??= false;
178
+ customBaseUrl ??= '';
179
+
180
+ const {
181
+ output: legacyOutput,
182
+ options: egressOptions,
183
+ fileOutputs,
184
+ streamOutputs,
185
+ segmentOutputs,
186
+ imageOutputs,
187
+ } = this.getOutputParams(output, options);
188
+
189
+ const req = new RoomCompositeEgressRequest({
190
+ roomName,
191
+ layout,
192
+ audioOnly,
193
+ videoOnly,
194
+ customBaseUrl,
195
+ output: legacyOutput,
196
+ options: egressOptions,
197
+ fileOutputs,
198
+ streamOutputs,
199
+ segmentOutputs,
200
+ imageOutputs,
201
+ }).toJson();
202
+
203
+ const data = await this.rpc.request(
204
+ svc,
205
+ 'StartRoomCompositeEgress',
206
+ req,
207
+ await this.authHeader({ roomRecord: true }),
208
+ );
209
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
210
+ }
211
+
212
+ /**
213
+ * @param url url
214
+ * @param output file or stream output
215
+ * @param opts WebOptions
216
+ */
217
+ async startWebEgress(
218
+ url: string,
219
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
220
+ opts?: WebOptions,
221
+ ): Promise<EgressInfo> {
222
+ const audioOnly = opts?.audioOnly || false;
223
+ const videoOnly = opts?.videoOnly || false;
224
+ const awaitStartSignal = opts?.awaitStartSignal || false;
225
+ const {
226
+ output: legacyOutput,
227
+ options,
228
+ fileOutputs,
229
+ streamOutputs,
230
+ segmentOutputs,
231
+ imageOutputs,
232
+ } = this.getOutputParams(output, opts?.encodingOptions);
233
+
234
+ const req = new WebEgressRequest({
235
+ url,
236
+ audioOnly,
237
+ videoOnly,
238
+ awaitStartSignal,
239
+ output: legacyOutput,
240
+ options,
241
+ fileOutputs,
242
+ streamOutputs,
243
+ segmentOutputs,
244
+ imageOutputs,
245
+ }).toJson();
246
+
247
+ const data = await this.rpc.request(
248
+ svc,
249
+ 'StartWebEgress',
250
+ req,
251
+ await this.authHeader({ roomRecord: true }),
252
+ );
253
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
254
+ }
255
+
256
+ /**
257
+ * Export a participant's audio and video tracks,
258
+ *
259
+ * @param roomName room name
260
+ * @param output one or more outputs
261
+ * @param opts ParticipantEgressOptions
262
+ */
263
+ async startParticipantEgress(
264
+ roomName: string,
265
+ identity: string,
266
+ output: EncodedOutputs,
267
+ opts?: ParticipantEgressOptions,
268
+ ): Promise<EgressInfo> {
269
+ const { options, fileOutputs, streamOutputs, segmentOutputs, imageOutputs } =
270
+ this.getOutputParams(output, opts?.encodingOptions);
271
+ const req = new ParticipantEgressRequest({
272
+ roomName,
273
+ identity,
274
+ options,
275
+ fileOutputs,
276
+ streamOutputs,
277
+ segmentOutputs,
278
+ imageOutputs,
279
+ }).toJson();
280
+
281
+ const data = await this.rpc.request(
282
+ svc,
283
+ 'StartParticipantEgress',
284
+ req,
285
+ this.authHeader({ roomRecord: true }),
286
+ );
287
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
288
+ }
289
+
290
+ /**
291
+ * @param roomName room name
292
+ * @param output file or stream output
293
+ * @param opts TrackCompositeOptions
294
+ */
295
+ async startTrackCompositeEgress(
296
+ roomName: string,
297
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
298
+ opts?: TrackCompositeOptions,
299
+ ): Promise<EgressInfo>;
300
+ /**
301
+ * @deprecated use TrackCompositeOptions instead
302
+ */
303
+ async startTrackCompositeEgress(
304
+ roomName: string,
305
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
306
+ audioTrackId?: string,
307
+ videoTrackId?: string,
308
+ options?: EncodingOptionsPreset | EncodingOptions,
309
+ ): Promise<EgressInfo>;
310
+ async startTrackCompositeEgress(
311
+ roomName: string,
312
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
313
+ optsOrAudioTrackId?: TrackCompositeOptions | string,
314
+ videoTrackId?: string,
315
+ options?: EncodingOptionsPreset | EncodingOptions,
316
+ ): Promise<EgressInfo> {
317
+ let audioTrackId: string | undefined;
318
+ if (optsOrAudioTrackId !== undefined) {
319
+ if (typeof optsOrAudioTrackId === 'string') {
320
+ audioTrackId = optsOrAudioTrackId;
321
+ } else {
322
+ const opts = <TrackCompositeOptions>optsOrAudioTrackId;
323
+ audioTrackId = opts.audioTrackId;
324
+ videoTrackId = opts.videoTrackId;
325
+ options = opts.encodingOptions;
326
+ }
327
+ }
328
+
329
+ audioTrackId ??= '';
330
+ videoTrackId ??= '';
331
+
332
+ const {
333
+ output: legacyOutput,
334
+ options: egressOptions,
335
+ fileOutputs,
336
+ streamOutputs,
337
+ segmentOutputs,
338
+ imageOutputs,
339
+ } = this.getOutputParams(output, options);
340
+ const req = new TrackCompositeEgressRequest({
341
+ roomName,
342
+ audioTrackId,
343
+ videoTrackId,
344
+ output: legacyOutput,
345
+ options: egressOptions,
346
+ fileOutputs,
347
+ streamOutputs,
348
+ segmentOutputs,
349
+ imageOutputs,
350
+ }).toJson();
351
+
352
+ const data = await this.rpc.request(
353
+ svc,
354
+ 'StartTrackCompositeEgress',
355
+ req,
356
+ await this.authHeader({ roomRecord: true }),
357
+ );
358
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
359
+ }
360
+
361
+ private isEncodedOutputs(output: any): output is EncodedOutputs {
362
+ return (
363
+ (<EncodedOutputs>output).file !== undefined ||
364
+ (<EncodedOutputs>output).stream !== undefined ||
365
+ (<EncodedOutputs>output).segments !== undefined
366
+ );
367
+ }
368
+
369
+ private isEncodedFileOutput(output: any): output is EncodedFileOutput {
370
+ return (
371
+ (<EncodedFileOutput>output).filepath !== undefined ||
372
+ (<EncodedFileOutput>output).fileType !== undefined
373
+ );
374
+ }
375
+
376
+ private isSegmentedFileOutput(output: any): output is SegmentedFileOutput {
377
+ return (
378
+ (<SegmentedFileOutput>output).filenamePrefix !== undefined ||
379
+ (<SegmentedFileOutput>output).playlistName !== undefined ||
380
+ (<SegmentedFileOutput>output).filenameSuffix !== undefined
381
+ );
382
+ }
383
+
384
+ private isStreamOutput(output: any): output is StreamOutput {
385
+ return (
386
+ (<StreamOutput>output).protocol !== undefined || (<StreamOutput>output).urls !== undefined
387
+ );
388
+ }
389
+
390
+ private getOutputParams(
391
+ output: EncodedOutputs | EncodedFileOutput | StreamOutput | SegmentedFileOutput,
392
+ opts?: EncodingOptionsPreset | EncodingOptions,
393
+ ) {
394
+ let file: EncodedFileOutput | undefined;
395
+ let fileOutputs: Array<EncodedFileOutput> | undefined;
396
+ let stream: StreamOutput | undefined;
397
+ let streamOutputs: Array<StreamOutput> | undefined;
398
+ let segments: SegmentedFileOutput | undefined;
399
+ let segmentOutputs: Array<SegmentedFileOutput> | undefined;
400
+ let imageOutputs: Array<ImageOutput> | undefined;
401
+
402
+ if (this.isEncodedOutputs(output)) {
403
+ if (output.file !== undefined) {
404
+ fileOutputs = [output.file];
405
+ }
406
+ if (output.stream !== undefined) {
407
+ streamOutputs = [output.stream];
408
+ }
409
+ if (output.segments !== undefined) {
410
+ segmentOutputs = [output.segments];
411
+ }
412
+ if (output.images !== undefined) {
413
+ imageOutputs = [output.images];
414
+ }
415
+ } else if (this.isEncodedFileOutput(output)) {
416
+ file = output;
417
+ fileOutputs = [file];
418
+ } else if (this.isSegmentedFileOutput(output)) {
419
+ segments = output;
420
+ segmentOutputs = [segments];
421
+ } else if (this.isStreamOutput(output)) {
422
+ stream = output;
423
+ streamOutputs = [stream];
424
+ }
425
+
426
+ let legacyOutput:
427
+ | {
428
+ value: EncodedFileOutput;
429
+ case: 'file';
430
+ }
431
+ | {
432
+ value: StreamOutput;
433
+ case: 'stream';
434
+ }
435
+ | {
436
+ value: SegmentedFileOutput;
437
+ case: 'segments';
438
+ }
439
+ | undefined;
440
+
441
+ if (file) {
442
+ legacyOutput = {
443
+ case: 'file',
444
+ value: file,
445
+ };
446
+ } else if (stream) {
447
+ legacyOutput = {
448
+ case: 'stream',
449
+ value: stream,
450
+ };
451
+ } else if (segments) {
452
+ legacyOutput = {
453
+ case: 'segments',
454
+ value: segments,
455
+ };
456
+ }
457
+ let egressOptions:
458
+ | {
459
+ value: EncodingOptionsPreset;
460
+ case: 'preset';
461
+ }
462
+ | {
463
+ value: EncodingOptions;
464
+ case: 'advanced';
465
+ }
466
+ | undefined;
467
+ if (opts) {
468
+ if (typeof opts === 'number') {
469
+ egressOptions = {
470
+ case: 'preset',
471
+ value: opts,
472
+ };
473
+ } else {
474
+ egressOptions = {
475
+ case: 'advanced',
476
+ value: <EncodingOptions>opts,
477
+ };
478
+ }
479
+ }
480
+
481
+ return {
482
+ output: legacyOutput,
483
+ options: egressOptions,
484
+ fileOutputs,
485
+ streamOutputs,
486
+ segmentOutputs,
487
+ imageOutputs,
488
+ };
489
+ }
490
+
491
+ /**
492
+ * @param roomName room name
493
+ * @param output file or websocket output
494
+ * @param trackId track Id
495
+ */
496
+ async startTrackEgress(
497
+ roomName: string,
498
+ output: DirectFileOutput | string,
499
+ trackId: string,
500
+ ): Promise<EgressInfo> {
501
+ let legacyOutput:
502
+ | {
503
+ value: DirectFileOutput;
504
+ case: 'file';
505
+ }
506
+ | {
507
+ value: string;
508
+ case: 'websocketUrl';
509
+ }
510
+ | undefined;
511
+
512
+ if (typeof output === 'string') {
513
+ legacyOutput = {
514
+ case: 'websocketUrl',
515
+ value: output,
516
+ };
517
+ } else {
518
+ legacyOutput = {
519
+ case: 'file',
520
+ value: output,
521
+ };
522
+ }
523
+
524
+ const req = new TrackEgressRequest({
525
+ roomName,
526
+ trackId,
527
+ output: legacyOutput,
528
+ }).toJson();
529
+
530
+ const data = await this.rpc.request(
531
+ svc,
532
+ 'StartTrackEgress',
533
+ req,
534
+ await this.authHeader({ roomRecord: true }),
535
+ );
536
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
537
+ }
538
+
539
+ /**
540
+ * @param egressId
541
+ * @param layout
542
+ */
543
+ async updateLayout(egressId: string, layout: string): Promise<EgressInfo> {
544
+ const data = await this.rpc.request(
545
+ svc,
546
+ 'UpdateLayout',
547
+ new UpdateLayoutRequest({ egressId, layout }).toJson(),
548
+ await this.authHeader({ roomRecord: true }),
549
+ );
550
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
551
+ }
552
+
553
+ /**
554
+ * @param egressId
555
+ * @param addOutputUrls
556
+ * @param removeOutputUrls
557
+ */
558
+ async updateStream(
559
+ egressId: string,
560
+ addOutputUrls?: string[],
561
+ removeOutputUrls?: string[],
562
+ ): Promise<EgressInfo> {
563
+ addOutputUrls ??= [];
564
+ removeOutputUrls ??= [];
565
+
566
+ const data = await this.rpc.request(
567
+ svc,
568
+ 'UpdateStream',
569
+ new UpdateStreamRequest({ egressId, addOutputUrls, removeOutputUrls }).toJson(),
570
+ await this.authHeader({ roomRecord: true }),
571
+ );
572
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
573
+ }
574
+
575
+ /**
576
+ * @param options options to filter listed Egresses, by default returns all
577
+ * Egress instances
578
+ */
579
+ async listEgress(options?: ListEgressOptions): Promise<Array<EgressInfo>>;
580
+ /**
581
+ * @deprecated
582
+ * @param roomName list egress for one room only
583
+ */
584
+ async listEgress(roomName?: string): Promise<Array<EgressInfo>>;
585
+ /**
586
+ * @param roomName list egress for one room only
587
+ */
588
+ async listEgress(options?: string | ListEgressOptions): Promise<Array<EgressInfo>> {
589
+ let req: Partial<ListEgressRequest> = {};
590
+ if (typeof options === 'string') {
591
+ req.roomName = options;
592
+ } else if (options !== undefined) {
593
+ req = options;
594
+ }
595
+
596
+ const data = await this.rpc.request(
597
+ svc,
598
+ 'ListEgress',
599
+ new ListEgressRequest(req).toJson(),
600
+ await this.authHeader({ roomRecord: true }),
601
+ );
602
+ return ListEgressResponse.fromJson(data, { ignoreUnknownFields: true }).items ?? [];
603
+ }
604
+
605
+ /**
606
+ * @param egressId
607
+ */
608
+ async stopEgress(egressId: string): Promise<EgressInfo> {
609
+ const data = await this.rpc.request(
610
+ svc,
611
+ 'StopEgress',
612
+ new StopEgressRequest({ egressId }).toJson(),
613
+ await this.authHeader({ roomRecord: true }),
614
+ );
615
+ return EgressInfo.fromJson(data, { ignoreUnknownFields: true });
616
+ }
617
+ }