@stream-io/video-client 0.3.0 → 0.3.2

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 (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/index.browser.es.js +863 -455
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +867 -453
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +863 -455
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +18 -1
  9. package/dist/src/devices/CameraManager.d.ts +31 -0
  10. package/dist/src/devices/CameraManagerState.d.ts +28 -0
  11. package/dist/src/devices/InputMediaDeviceManager.d.ts +47 -0
  12. package/dist/src/devices/InputMediaDeviceManagerState.d.ts +69 -0
  13. package/dist/src/devices/MicrophoneManager.d.ts +19 -0
  14. package/dist/src/devices/MicrophoneManagerState.d.ts +4 -0
  15. package/dist/src/devices/__tests__/CameraManager.test.d.ts +1 -0
  16. package/dist/src/devices/__tests__/InputMediaDeviceManager.test.d.ts +1 -0
  17. package/dist/src/devices/__tests__/MicrophoneManager.test.d.ts +1 -0
  18. package/dist/src/devices/__tests__/mocks.d.ts +13 -0
  19. package/dist/src/devices/index.d.ts +6 -0
  20. package/dist/src/types.d.ts +4 -0
  21. package/dist/version.d.ts +1 -1
  22. package/package.json +1 -1
  23. package/src/Call.ts +100 -3
  24. package/src/__tests__/StreamVideoClient.test.ts +3 -0
  25. package/src/devices/CameraManager.ts +73 -0
  26. package/src/devices/CameraManagerState.ts +61 -0
  27. package/src/devices/InputMediaDeviceManager.ts +121 -0
  28. package/src/devices/InputMediaDeviceManagerState.ts +111 -0
  29. package/src/devices/MicrophoneManager.ts +45 -0
  30. package/src/devices/MicrophoneManagerState.ts +9 -0
  31. package/src/devices/__tests__/CameraManager.test.ts +150 -0
  32. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +159 -0
  33. package/src/devices/__tests__/MicrophoneManager.test.ts +103 -0
  34. package/src/devices/__tests__/mocks.ts +98 -0
  35. package/src/devices/index.ts +6 -0
  36. package/src/rtc/Publisher.ts +11 -19
  37. package/src/rtc/videoLayers.ts +7 -2
  38. package/src/types.ts +4 -0
@@ -4,11 +4,11 @@ import { ServiceType, stackIntercept } from '@protobuf-ts/runtime-rpc';
4
4
  import axios, { AxiosHeaders } from 'axios';
5
5
  export { AxiosError } from 'axios';
6
6
  import { TwirpFetchTransport } from '@protobuf-ts/twirp-transport';
7
- import { ReplaySubject, BehaviorSubject, takeWhile, filter, pairwise, tap, debounce, timer, map as map$2, Observable, debounceTime, concatMap, from, shareReplay, merge, combineLatest } from 'rxjs';
7
+ import { ReplaySubject, BehaviorSubject, distinctUntilChanged as distinctUntilChanged$1, Observable, debounceTime, concatMap, from, shareReplay, merge, map as map$2, combineLatest, filter, pairwise, takeWhile, tap, debounce, timer } from 'rxjs';
8
8
  import * as SDP from 'sdp-transform';
9
+ import { UAParser } from 'ua-parser-js';
9
10
  import WebSocket from 'isomorphic-ws';
10
11
  import { take, map as map$1, distinctUntilChanged } from 'rxjs/operators';
11
- import { UAParser } from 'ua-parser-js';
12
12
  import { fromByteArray } from 'base64-js';
13
13
 
14
14
  /**
@@ -6266,6 +6266,56 @@ function getIceCandidate(candidate) {
6266
6266
  }
6267
6267
  }
6268
6268
 
6269
+ let sdkInfo;
6270
+ let osInfo;
6271
+ let deviceInfo;
6272
+ const setSdkInfo = (info) => {
6273
+ sdkInfo = info;
6274
+ };
6275
+ const getSdkInfo = () => {
6276
+ return sdkInfo;
6277
+ };
6278
+ const setOSInfo = (info) => {
6279
+ osInfo = info;
6280
+ };
6281
+ const getOSInfo = () => {
6282
+ return osInfo;
6283
+ };
6284
+ const setDeviceInfo = (info) => {
6285
+ deviceInfo = info;
6286
+ };
6287
+ const getDeviceInfo = () => {
6288
+ return deviceInfo;
6289
+ };
6290
+ const getClientDetails = () => {
6291
+ if (isReactNative()) {
6292
+ // Since RN doesn't support web, sharing browser info is not required
6293
+ return {
6294
+ sdk: getSdkInfo(),
6295
+ os: getOSInfo(),
6296
+ device: getDeviceInfo(),
6297
+ };
6298
+ }
6299
+ const userAgent = new UAParser(navigator.userAgent);
6300
+ const { browser, os, device, cpu } = userAgent.getResult();
6301
+ return {
6302
+ sdk: getSdkInfo(),
6303
+ browser: {
6304
+ name: browser.name || navigator.userAgent,
6305
+ version: browser.version || '',
6306
+ },
6307
+ os: {
6308
+ name: os.name || '',
6309
+ version: os.version || '',
6310
+ architecture: cpu.architecture || '',
6311
+ },
6312
+ device: {
6313
+ name: `${device.vendor || ''} ${device.model || ''} ${device.type || ''}`,
6314
+ version: '',
6315
+ },
6316
+ };
6317
+ };
6318
+
6269
6319
  const DEFAULT_BITRATE = 1250000;
6270
6320
  const defaultTargetResolution = {
6271
6321
  bitrate: DEFAULT_BITRATE,
@@ -6280,9 +6330,11 @@ const defaultTargetResolution = {
6280
6330
  * @param targetResolution the expected target resolution.
6281
6331
  */
6282
6332
  const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetResolution) => {
6333
+ var _a;
6283
6334
  const optimalVideoLayers = [];
6284
6335
  const settings = videoTrack.getSettings();
6285
6336
  const { width: w = 0, height: h = 0 } = settings;
6337
+ const isRNIos = isReactNative() && ((_a = getOSInfo()) === null || _a === void 0 ? void 0 : _a.name.toLowerCase()) === 'ios';
6286
6338
  const maxBitrate = getComputedMaxBitrate(targetResolution, w, h);
6287
6339
  let downscaleFactor = 1;
6288
6340
  ['f', 'h', 'q'].forEach((rid) => {
@@ -6296,10 +6348,11 @@ const findOptimalVideoLayers = (videoTrack, targetResolution = defaultTargetReso
6296
6348
  height: Math.round(h / downscaleFactor),
6297
6349
  maxBitrate: Math.round(maxBitrate / downscaleFactor),
6298
6350
  scaleResolutionDownBy: downscaleFactor,
6351
+ // Simulcast on iOS React-Native requires all encodings to share the same framerate
6299
6352
  maxFramerate: {
6300
6353
  f: 30,
6301
- h: 25,
6302
- q: 20,
6354
+ h: isRNIos ? 30 : 25,
6355
+ q: isRNIos ? 30 : 20,
6303
6356
  }[rid],
6304
6357
  });
6305
6358
  downscaleFactor *= 2;
@@ -6498,6 +6551,7 @@ class Publisher {
6498
6551
  * @param opts
6499
6552
  */
6500
6553
  this.publishStream = (mediaStream, track, trackType, opts = {}) => __awaiter(this, void 0, void 0, function* () {
6554
+ var _a;
6501
6555
  if (track.readyState === 'ended') {
6502
6556
  throw new Error(`Can't publish a track that has ended already.`);
6503
6557
  }
@@ -6525,7 +6579,14 @@ class Publisher {
6525
6579
  const videoEncodings = trackType === TrackType.VIDEO
6526
6580
  ? findOptimalVideoLayers(track, targetResolution)
6527
6581
  : undefined;
6528
- const codecPreferences = this.getCodecPreferences(trackType, opts.preferredCodec);
6582
+ let preferredCodec = opts.preferredCodec;
6583
+ if (!preferredCodec && trackType === TrackType.VIDEO) {
6584
+ const isRNAndroid = isReactNative() && ((_a = getOSInfo()) === null || _a === void 0 ? void 0 : _a.name.toLowerCase()) === 'android';
6585
+ if (isRNAndroid) {
6586
+ preferredCodec = 'VP8';
6587
+ }
6588
+ }
6589
+ const codecPreferences = this.getCodecPreferences(trackType, preferredCodec);
6529
6590
  // listen for 'ended' event on the track as it might be ended abruptly
6530
6591
  // by an external factor as permission revokes, device disconnected, etc.
6531
6592
  // keep in mind that `track.stop()` doesn't trigger this event.
@@ -6619,9 +6680,9 @@ class Publisher {
6619
6680
  });
6620
6681
  };
6621
6682
  this.updateVideoPublishQuality = (enabledRids) => __awaiter(this, void 0, void 0, function* () {
6622
- var _a;
6683
+ var _b;
6623
6684
  logger$3('info', 'Update publish quality, requested rids by SFU:', enabledRids);
6624
- const videoSender = (_a = this.transceiverRegistry[TrackType.VIDEO]) === null || _a === void 0 ? void 0 : _a.sender;
6685
+ const videoSender = (_b = this.transceiverRegistry[TrackType.VIDEO]) === null || _b === void 0 ? void 0 : _b.sender;
6625
6686
  if (!videoSender) {
6626
6687
  logger$3('warn', 'Update publish quality, no video sender found.');
6627
6688
  return;
@@ -6719,8 +6780,8 @@ class Publisher {
6719
6780
  * @param options the optional offer options to use.
6720
6781
  */
6721
6782
  this.negotiate = (options) => __awaiter(this, void 0, void 0, function* () {
6722
- var _b;
6723
- this.isIceRestarting = (_b = options === null || options === void 0 ? void 0 : options.iceRestart) !== null && _b !== void 0 ? _b : false;
6783
+ var _c;
6784
+ this.isIceRestarting = (_c = options === null || options === void 0 ? void 0 : options.iceRestart) !== null && _c !== void 0 ? _c : false;
6724
6785
  const offer = yield this.pc.createOffer(options);
6725
6786
  offer.sdp = this.mungeCodecs(offer.sdp);
6726
6787
  const trackInfos = this.getCurrentTrackInfos(offer.sdp);
@@ -6758,15 +6819,6 @@ class Publisher {
6758
6819
  this.mungeCodecs = (sdp) => {
6759
6820
  if (sdp) {
6760
6821
  sdp = toggleDtx(sdp, this.isDtxEnabled);
6761
- if (isReactNative()) {
6762
- if (this.preferredVideoCodec) {
6763
- sdp = setPreferredCodec(sdp, 'video', this.preferredVideoCodec);
6764
- }
6765
- sdp = setPreferredCodec(sdp, 'audio', this.isRedEnabled ? 'red' : 'opus');
6766
- if (!this.isRedEnabled) {
6767
- sdp = removeCodec(sdp, 'audio', 'red');
6768
- }
6769
- }
6770
6822
  }
6771
6823
  return sdp;
6772
6824
  };
@@ -9464,195 +9516,730 @@ const CallTypes = new CallTypesRegistry([
9464
9516
  }),
9465
9517
  ]);
9466
9518
 
9467
- let sdkInfo;
9468
- let osInfo;
9469
- let deviceInfo;
9470
- const setSdkInfo = (info) => {
9471
- sdkInfo = info;
9472
- };
9473
- const getSdkInfo = () => {
9474
- return sdkInfo;
9475
- };
9476
- const setOSInfo = (info) => {
9477
- osInfo = info;
9478
- };
9479
- const getOSInfo = () => {
9480
- return osInfo;
9481
- };
9482
- const setDeviceInfo = (info) => {
9483
- deviceInfo = info;
9484
- };
9485
- const getDeviceInfo = () => {
9486
- return deviceInfo;
9487
- };
9488
- const getClientDetails = () => {
9489
- if (isReactNative()) {
9490
- // Since RN doesn't support web, sharing browser info is not required
9491
- return {
9492
- sdk: getSdkInfo(),
9493
- os: getOSInfo(),
9494
- device: getDeviceInfo(),
9495
- };
9496
- }
9497
- const userAgent = new UAParser(navigator.userAgent);
9498
- const { browser, os, device, cpu } = userAgent.getResult();
9499
- return {
9500
- sdk: getSdkInfo(),
9501
- browser: {
9502
- name: browser.name || navigator.userAgent,
9503
- version: browser.version || '',
9504
- },
9505
- os: {
9506
- name: os.name || '',
9507
- version: os.version || '',
9508
- architecture: cpu.architecture || '',
9509
- },
9510
- device: {
9511
- name: `${device.vendor || ''} ${device.model || ''} ${device.type || ''}`,
9512
- version: '',
9513
- },
9514
- };
9515
- };
9516
-
9517
- /**
9518
- * An object representation of a `Call`.
9519
- */
9520
- class Call {
9521
- /**
9522
- * Constructs a new `Call` instance.
9523
- *
9524
- * NOTE: Don't call the constructor directly, instead
9525
- * Use the [`StreamVideoClient.call`](./StreamVideoClient.md/#call)
9526
- * method to construct a `Call` instance.
9527
- */
9528
- constructor({ type, id, streamClient, members, ownCapabilities, sortParticipantsBy, clientStore, ringing = false, watching = false, }) {
9529
- /**
9530
- * ViewportTracker instance
9531
- */
9532
- this.viewportTracker = new ViewportTracker();
9533
- /**
9534
- * The state of this call.
9535
- */
9536
- this.state = new CallState();
9537
- /**
9538
- * The permissions context of this call.
9539
- */
9540
- this.permissionsContext = new PermissionsContext();
9541
- /**
9542
- * The event dispatcher instance dedicated to this Call instance.
9543
- * @private
9544
- */
9545
- this.dispatcher = new Dispatcher();
9546
- this.trackSubscriptionsSubject = new BehaviorSubject({ type: DebounceType.MEDIUM, data: [] });
9547
- this.reconnectAttempts = 0;
9548
- this.maxReconnectAttempts = 10;
9549
- /**
9550
- * A list hooks/functions to invoke when the call is left.
9551
- * A typical use case is to clean up some global event handlers.
9552
- * @private
9553
- */
9554
- this.leaveCallHooks = [];
9555
- this.streamClientEventHandlers = new Map();
9556
- /**
9557
- * Leave the call and stop the media streams that were published by the call.
9558
- */
9559
- this.leave = ({ reject = false } = {}) => __awaiter(this, void 0, void 0, function* () {
9560
- var _a, _b, _c, _d;
9561
- const callingState = this.state.callingState;
9562
- if (callingState === CallingState.LEFT) {
9563
- throw new Error('Cannot leave call that has already been left.');
9564
- }
9565
- if (callingState === CallingState.JOINING) {
9566
- yield this.assertCallJoined();
9567
- }
9568
- if (this.ringing) {
9569
- // I'm the one who started the call, so I should cancel it.
9570
- const hasOtherParticipants = this.state.remoteParticipants.length > 0;
9571
- if (this.isCreatedByMe && !hasOtherParticipants) {
9572
- // Signals other users that I have cancelled my call to them
9573
- // before they accepted it.
9574
- yield this.reject();
9575
- }
9576
- else if (reject && callingState === CallingState.RINGING) {
9577
- // Signals other users that I have rejected the incoming call.
9578
- yield this.reject();
9579
- }
9580
- }
9581
- (_a = this.statsReporter) === null || _a === void 0 ? void 0 : _a.stop();
9582
- this.statsReporter = undefined;
9583
- (_b = this.subscriber) === null || _b === void 0 ? void 0 : _b.close();
9584
- this.subscriber = undefined;
9585
- (_c = this.publisher) === null || _c === void 0 ? void 0 : _c.close();
9586
- this.publisher = undefined;
9587
- (_d = this.sfuClient) === null || _d === void 0 ? void 0 : _d.close();
9588
- this.sfuClient = undefined;
9589
- this.dispatcher.offAll();
9590
- // Call all leave call hooks, e.g. to clean up global event handlers
9591
- this.leaveCallHooks.forEach((hook) => hook());
9592
- this.clientStore.unregisterCall(this);
9593
- this.state.setCallingState(CallingState.LEFT);
9594
- });
9519
+ class InputMediaDeviceManagerState {
9520
+ constructor() {
9521
+ this.statusSubject = new BehaviorSubject(undefined);
9522
+ this.mediaStreamSubject = new BehaviorSubject(undefined);
9523
+ this.selectedDeviceSubject = new BehaviorSubject(undefined);
9595
9524
  /**
9596
- * Loads the information about the call.
9525
+ * Gets the current value of an observable, or undefined if the observable has
9526
+ * not emitted a value yet.
9597
9527
  *
9598
- * @param params.ring if set to true, a `call.ring` event will be sent to the call members.
9599
- * @param params.notify if set to true, a `call.notification` event will be sent to the call members.
9600
- * @param params.members_limit the members limit.
9528
+ * @param observable$ the observable to get the value from.
9601
9529
  */
9602
- this.get = (params) => __awaiter(this, void 0, void 0, function* () {
9603
- const response = yield this.streamClient.get(this.streamClientBasePath, params);
9604
- if ((params === null || params === void 0 ? void 0 : params.ring) && !this.ringing) {
9605
- this.ringingSubject.next(true);
9606
- }
9607
- this.state.updateFromCallResponse(response.call);
9608
- this.state.setMembers(response.members);
9609
- this.state.setOwnCapabilities(response.own_capabilities);
9610
- if (this.streamClient._hasConnectionID()) {
9611
- this.watching = true;
9612
- this.clientStore.registerCall(this);
9613
- }
9614
- return response;
9615
- });
9530
+ this.getCurrentValue = getCurrentValue;
9616
9531
  /**
9617
- * Loads the information about the call and creates it if it doesn't exist.
9532
+ * Updates the value of the provided Subject.
9533
+ * An `update` can either be a new value or a function which takes
9534
+ * the current value and returns a new value.
9618
9535
  *
9619
- * @param data the data to create the call with.
9620
- */
9621
- this.getOrCreate = (data) => __awaiter(this, void 0, void 0, function* () {
9622
- const response = yield this.streamClient.post(this.streamClientBasePath, data);
9623
- if ((data === null || data === void 0 ? void 0 : data.ring) && !this.ringing) {
9624
- this.ringingSubject.next(true);
9625
- }
9626
- this.state.updateFromCallResponse(response.call);
9627
- this.state.setMembers(response.members);
9628
- this.state.setOwnCapabilities(response.own_capabilities);
9629
- if (this.streamClient._hasConnectionID()) {
9630
- this.watching = true;
9631
- this.clientStore.registerCall(this);
9632
- }
9633
- return response;
9634
- });
9635
- /**
9636
- * Creates a call
9536
+ * @internal
9637
9537
  *
9638
- * @param data the data to create the call with.
9639
- */
9640
- this.create = (data) => __awaiter(this, void 0, void 0, function* () {
9641
- return this.getOrCreate(data);
9642
- });
9643
- /**
9644
- * A shortcut for {@link Call.get} with `ring` parameter set to `true`.
9645
- * Will send a `call.ring` event to the call members.
9646
- */
9647
- this.ring = () => __awaiter(this, void 0, void 0, function* () {
9648
- return yield this.get({ ring: true });
9649
- });
9650
- /**
9651
- * A shortcut for {@link Call.get} with `notify` parameter set to `true`.
9652
- * Will send a `call.notification` event to the call members.
9538
+ * @param subject the subject to update.
9539
+ * @param update the update to apply to the subject.
9540
+ * @return the updated value.
9653
9541
  */
9654
- this.notify = () => __awaiter(this, void 0, void 0, function* () {
9655
- return yield this.get({ notify: true });
9542
+ this.setCurrentValue = setCurrentValue;
9543
+ this.mediaStream$ = this.mediaStreamSubject.asObservable();
9544
+ this.selectedDevice$ = this.selectedDeviceSubject
9545
+ .asObservable()
9546
+ .pipe(distinctUntilChanged$1());
9547
+ this.status$ = this.statusSubject.asObservable();
9548
+ }
9549
+ /**
9550
+ * The device status
9551
+ */
9552
+ get status() {
9553
+ return this.getCurrentValue(this.status$);
9554
+ }
9555
+ /**
9556
+ * The currently selected device
9557
+ */
9558
+ get selectedDevice() {
9559
+ return this.getCurrentValue(this.selectedDevice$);
9560
+ }
9561
+ /**
9562
+ * The current media stream, or `undefined` if the device is currently disabled.
9563
+ */
9564
+ get mediaStream() {
9565
+ return this.getCurrentValue(this.mediaStream$);
9566
+ }
9567
+ /**
9568
+ * @internal
9569
+ * @param status
9570
+ */
9571
+ setStatus(status) {
9572
+ this.setCurrentValue(this.statusSubject, status);
9573
+ }
9574
+ /**
9575
+ * @internal
9576
+ * @param stream
9577
+ */
9578
+ setMediaStream(stream) {
9579
+ this.setCurrentValue(this.mediaStreamSubject, stream);
9580
+ if (stream) {
9581
+ this.setDevice(this.getDeviceIdFromStream(stream));
9582
+ }
9583
+ }
9584
+ /**
9585
+ * @internal
9586
+ * @param stream
9587
+ */
9588
+ setDevice(deviceId) {
9589
+ this.setCurrentValue(this.selectedDeviceSubject, deviceId);
9590
+ }
9591
+ }
9592
+
9593
+ class CameraManagerState extends InputMediaDeviceManagerState {
9594
+ constructor() {
9595
+ super();
9596
+ this.directionSubject = new BehaviorSubject(undefined);
9597
+ this.direction$ = this.directionSubject
9598
+ .asObservable()
9599
+ .pipe(distinctUntilChanged$1());
9600
+ }
9601
+ /**
9602
+ * The preferred camera direction
9603
+ * front - means the camera facing the user
9604
+ * back - means the camera facing the environment
9605
+ */
9606
+ get direction() {
9607
+ return this.getCurrentValue(this.direction$);
9608
+ }
9609
+ /**
9610
+ * @internal
9611
+ */
9612
+ setDirection(direction) {
9613
+ this.setCurrentValue(this.directionSubject, direction);
9614
+ }
9615
+ /**
9616
+ * @internal
9617
+ */
9618
+ setMediaStream(stream) {
9619
+ var _a;
9620
+ super.setMediaStream(stream);
9621
+ if (stream) {
9622
+ // RN getSettings() doesn't return facingMode, so we don't verify camera direction
9623
+ const direction = isReactNative()
9624
+ ? this.direction
9625
+ : ((_a = stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().facingMode) === 'environment'
9626
+ ? 'back'
9627
+ : 'front';
9628
+ this.setDirection(direction);
9629
+ }
9630
+ }
9631
+ getDeviceIdFromStream(stream) {
9632
+ var _a;
9633
+ return (_a = stream.getVideoTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
9634
+ }
9635
+ }
9636
+
9637
+ const getDevices = (constraints) => {
9638
+ return new Observable((subscriber) => {
9639
+ navigator.mediaDevices
9640
+ .getUserMedia(constraints)
9641
+ .then((media) => {
9642
+ // in Firefox, devices can be enumerated after userMedia is requested
9643
+ // and permissions granted. Otherwise, device labels are empty
9644
+ navigator.mediaDevices.enumerateDevices().then((devices) => {
9645
+ subscriber.next(devices);
9646
+ // If we stop the tracks before enumerateDevices -> the labels won't show up in Firefox
9647
+ disposeOfMediaStream(media);
9648
+ subscriber.complete();
9649
+ });
9650
+ })
9651
+ .catch((error) => {
9652
+ getLogger(['devices'])('error', 'Failed to get devices', error);
9653
+ subscriber.error(error);
9654
+ });
9655
+ });
9656
+ };
9657
+ /**
9658
+ * [Tells if the browser supports audio output change on 'audio' elements](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
9659
+ *
9660
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9661
+ */
9662
+ const checkIfAudioOutputChangeSupported = () => {
9663
+ if (typeof document === 'undefined')
9664
+ return false;
9665
+ const element = document.createElement('audio');
9666
+ return element.sinkId !== undefined;
9667
+ };
9668
+ /**
9669
+ * The default constraints used to request audio devices.
9670
+ */
9671
+ const audioDeviceConstraints = {
9672
+ audio: {
9673
+ autoGainControl: true,
9674
+ noiseSuppression: true,
9675
+ echoCancellation: true,
9676
+ },
9677
+ };
9678
+ /**
9679
+ * The default constraints used to request video devices.
9680
+ */
9681
+ const videoDeviceConstraints = {
9682
+ video: {
9683
+ width: 1280,
9684
+ height: 720,
9685
+ },
9686
+ };
9687
+ // Audio and video devices are requested in two separate requests: that way users will be presented with two separate prompts -> they can give access to just camera, or just microphone
9688
+ const deviceChange$ = new Observable((subscriber) => {
9689
+ var _a, _b;
9690
+ const deviceChangeHandler = () => subscriber.next();
9691
+ (_b = (_a = navigator.mediaDevices).addEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, 'devicechange', deviceChangeHandler);
9692
+ return () => {
9693
+ var _a, _b;
9694
+ return (_b = (_a = navigator.mediaDevices).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, 'devicechange', deviceChangeHandler);
9695
+ };
9696
+ }).pipe(debounceTime(500), concatMap(() => from(navigator.mediaDevices.enumerateDevices())), shareReplay(1));
9697
+ const audioDevices$ = merge(getDevices(audioDeviceConstraints), deviceChange$).pipe(shareReplay(1));
9698
+ const videoDevices$ = merge(getDevices(videoDeviceConstraints), deviceChange$).pipe(shareReplay(1));
9699
+ /**
9700
+ * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audioinput' devices, if devices are added/removed the list is updated.
9701
+ *
9702
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9703
+ * @returns
9704
+ */
9705
+ const getAudioDevices = () => audioDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'audioinput')));
9706
+ /**
9707
+ * Prompts the user for a permission to use video devices (if not already granted) and lists the available 'videoinput' devices, if devices are added/removed the list is updated.
9708
+ *
9709
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9710
+ * @returns
9711
+ */
9712
+ const getVideoDevices = () => videoDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'videoinput' && d.deviceId.length)));
9713
+ /**
9714
+ * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audiooutput' devices, if devices are added/removed the list is updated. Selecting 'audiooutput' device only makes sense if [the browser has support for changing audio output on 'audio' elements](#checkifaudiooutputchangesupported)
9715
+ *
9716
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9717
+ * @returns
9718
+ */
9719
+ const getAudioOutputDevices = () => {
9720
+ return audioDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'audiooutput')));
9721
+ };
9722
+ const getStream = (constraints) => __awaiter(void 0, void 0, void 0, function* () {
9723
+ try {
9724
+ return yield navigator.mediaDevices.getUserMedia(constraints);
9725
+ }
9726
+ catch (e) {
9727
+ getLogger(['devices'])('error', `Failed get user media`, {
9728
+ error: e,
9729
+ constraints: constraints,
9730
+ });
9731
+ throw e;
9732
+ }
9733
+ });
9734
+ /**
9735
+ * Returns an audio media stream that fulfills the given constraints.
9736
+ * If no constraints are provided, it uses the browser's default ones.
9737
+ *
9738
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9739
+ * @param trackConstraints the constraints to use when requesting the stream.
9740
+ * @returns the new `MediaStream` fulfilling the given constraints.
9741
+ */
9742
+ const getAudioStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, function* () {
9743
+ const constraints = {
9744
+ audio: Object.assign(Object.assign({}, audioDeviceConstraints.audio), trackConstraints),
9745
+ };
9746
+ return getStream(constraints);
9747
+ });
9748
+ /**
9749
+ * Returns a video media stream that fulfills the given constraints.
9750
+ * If no constraints are provided, it uses the browser's default ones.
9751
+ *
9752
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9753
+ * @param trackConstraints the constraints to use when requesting the stream.
9754
+ * @returns a new `MediaStream` fulfilling the given constraints.
9755
+ */
9756
+ const getVideoStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, function* () {
9757
+ const constraints = {
9758
+ video: Object.assign(Object.assign({}, videoDeviceConstraints.video), trackConstraints),
9759
+ };
9760
+ return getStream(constraints);
9761
+ });
9762
+ /**
9763
+ * Prompts the user for a permission to share a screen.
9764
+ * If the user grants the permission, a screen sharing stream is returned. Throws otherwise.
9765
+ *
9766
+ * The callers of this API are responsible to handle the possible errors.
9767
+ *
9768
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9769
+ *
9770
+ * @param options any additional options to pass to the [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) API.
9771
+ */
9772
+ const getScreenShareStream = (options) => __awaiter(void 0, void 0, void 0, function* () {
9773
+ try {
9774
+ return yield navigator.mediaDevices.getDisplayMedia(Object.assign({ video: true, audio: false }, options));
9775
+ }
9776
+ catch (e) {
9777
+ getLogger(['devices'])('error', 'Failed to get screen share stream', e);
9778
+ throw e;
9779
+ }
9780
+ });
9781
+ const watchForDisconnectedDevice = (kind, deviceId$) => {
9782
+ let devices$;
9783
+ switch (kind) {
9784
+ case 'audioinput':
9785
+ devices$ = getAudioDevices();
9786
+ break;
9787
+ case 'videoinput':
9788
+ devices$ = getVideoDevices();
9789
+ break;
9790
+ case 'audiooutput':
9791
+ devices$ = getAudioOutputDevices();
9792
+ break;
9793
+ }
9794
+ return combineLatest([devices$, deviceId$]).pipe(filter(([devices, deviceId]) => !!deviceId && !devices.find((d) => d.deviceId === deviceId)), map$2(() => true));
9795
+ };
9796
+ /**
9797
+ * Notifies the subscriber if a given 'audioinput' device is disconnected
9798
+ *
9799
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9800
+ * @param deviceId$ an Observable that specifies which device to watch for
9801
+ * @returns
9802
+ */
9803
+ const watchForDisconnectedAudioDevice = (deviceId$) => {
9804
+ return watchForDisconnectedDevice('audioinput', deviceId$);
9805
+ };
9806
+ /**
9807
+ * Notifies the subscriber if a given 'videoinput' device is disconnected
9808
+ *
9809
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9810
+ * @param deviceId$ an Observable that specifies which device to watch for
9811
+ * @returns
9812
+ */
9813
+ const watchForDisconnectedVideoDevice = (deviceId$) => {
9814
+ return watchForDisconnectedDevice('videoinput', deviceId$);
9815
+ };
9816
+ /**
9817
+ * Notifies the subscriber if a given 'audiooutput' device is disconnected
9818
+ *
9819
+ * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
9820
+ * @param deviceId$ an Observable that specifies which device to watch for
9821
+ * @returns
9822
+ */
9823
+ const watchForDisconnectedAudioOutputDevice = (deviceId$) => {
9824
+ return watchForDisconnectedDevice('audiooutput', deviceId$);
9825
+ };
9826
+ const watchForAddedDefaultDevice = (kind) => {
9827
+ let devices$;
9828
+ switch (kind) {
9829
+ case 'audioinput':
9830
+ devices$ = getAudioDevices();
9831
+ break;
9832
+ case 'videoinput':
9833
+ devices$ = getVideoDevices();
9834
+ break;
9835
+ case 'audiooutput':
9836
+ devices$ = getAudioOutputDevices();
9837
+ break;
9838
+ default:
9839
+ throw new Error('Unknown MediaDeviceKind', kind);
9840
+ }
9841
+ return devices$.pipe(pairwise(), filter(([prev, current]) => {
9842
+ const prevDefault = prev.find((device) => device.deviceId === 'default');
9843
+ const currentDefault = current.find((device) => device.deviceId === 'default');
9844
+ return !!(current.length > prev.length &&
9845
+ prevDefault &&
9846
+ currentDefault &&
9847
+ prevDefault.groupId !== currentDefault.groupId);
9848
+ }), map$2(() => true));
9849
+ };
9850
+ /**
9851
+ * Notifies the subscriber about newly added default audio input device.
9852
+ * @returns Observable<boolean>
9853
+ */
9854
+ const watchForAddedDefaultAudioDevice = () => watchForAddedDefaultDevice('audioinput');
9855
+ /**
9856
+ * Notifies the subscriber about newly added default audio output device.
9857
+ * @returns Observable<boolean>
9858
+ */
9859
+ const watchForAddedDefaultAudioOutputDevice = () => watchForAddedDefaultDevice('audiooutput');
9860
+ /**
9861
+ * Notifies the subscriber about newly added default video input device.
9862
+ * @returns Observable<boolean>
9863
+ */
9864
+ const watchForAddedDefaultVideoDevice = () => watchForAddedDefaultDevice('videoinput');
9865
+ /**
9866
+ * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
9867
+ *
9868
+ * @param stream MediaStream
9869
+ * @returns void
9870
+ */
9871
+ const disposeOfMediaStream = (stream) => {
9872
+ if (!stream.active)
9873
+ return;
9874
+ stream.getTracks().forEach((track) => {
9875
+ track.stop();
9876
+ stream.removeTrack(track);
9877
+ });
9878
+ // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
9879
+ if (typeof stream.release === 'function') {
9880
+ // @ts-expect-error
9881
+ stream.release();
9882
+ }
9883
+ };
9884
+
9885
+ class InputMediaDeviceManager {
9886
+ constructor(call, state) {
9887
+ this.call = call;
9888
+ this.state = state;
9889
+ }
9890
+ /**
9891
+ * Lists the available audio/video devices
9892
+ *
9893
+ * Note: It prompts the user for a permission to use devices (if not already granted)
9894
+ *
9895
+ * @returns an Observable that will be updated if a device is connected or disconnected
9896
+ */
9897
+ listDevices() {
9898
+ return this.getDevices();
9899
+ }
9900
+ /**
9901
+ * Starts camera/microphone
9902
+ */
9903
+ enable() {
9904
+ return __awaiter(this, void 0, void 0, function* () {
9905
+ if (this.state.status === 'enabled') {
9906
+ return;
9907
+ }
9908
+ yield this.startStream();
9909
+ this.state.setStatus('enabled');
9910
+ });
9911
+ }
9912
+ /**
9913
+ * Stops camera/microphone
9914
+ * @returns
9915
+ */
9916
+ disable() {
9917
+ return __awaiter(this, void 0, void 0, function* () {
9918
+ if (this.state.status === 'disabled') {
9919
+ return;
9920
+ }
9921
+ yield this.stopStream();
9922
+ this.state.setStatus('disabled');
9923
+ });
9924
+ }
9925
+ /**
9926
+ * If current device statis is disabled, it will enable the device, else it will disable it.
9927
+ * @returns
9928
+ */
9929
+ toggle() {
9930
+ return __awaiter(this, void 0, void 0, function* () {
9931
+ if (this.state.status === 'enabled') {
9932
+ return this.disable();
9933
+ }
9934
+ else {
9935
+ return this.enable();
9936
+ }
9937
+ });
9938
+ }
9939
+ /**
9940
+ * Select device
9941
+ *
9942
+ * Note: this method is not supported in React Native
9943
+ *
9944
+ * @param deviceId
9945
+ */
9946
+ select(deviceId) {
9947
+ return __awaiter(this, void 0, void 0, function* () {
9948
+ if (isReactNative()) {
9949
+ throw new Error('This method is not supported in React Native');
9950
+ }
9951
+ if (deviceId === this.state.selectedDevice) {
9952
+ return;
9953
+ }
9954
+ this.state.setDevice(deviceId);
9955
+ yield this.applySettingsToStream();
9956
+ });
9957
+ }
9958
+ applySettingsToStream() {
9959
+ return __awaiter(this, void 0, void 0, function* () {
9960
+ if (this.state.status === 'enabled') {
9961
+ yield this.stopStream();
9962
+ yield this.startStream();
9963
+ }
9964
+ });
9965
+ }
9966
+ stopStream() {
9967
+ return __awaiter(this, void 0, void 0, function* () {
9968
+ if (!this.state.mediaStream) {
9969
+ return;
9970
+ }
9971
+ if (this.call.state.callingState === CallingState.JOINED) {
9972
+ yield this.stopPublishStream();
9973
+ }
9974
+ else if (this.state.mediaStream) {
9975
+ disposeOfMediaStream(this.state.mediaStream);
9976
+ }
9977
+ this.state.setMediaStream(undefined);
9978
+ });
9979
+ }
9980
+ startStream() {
9981
+ return __awaiter(this, void 0, void 0, function* () {
9982
+ if (this.state.mediaStream) {
9983
+ return;
9984
+ }
9985
+ const constraints = { deviceId: this.state.selectedDevice };
9986
+ const stream = yield this.getStream(constraints);
9987
+ if (this.call.state.callingState === CallingState.JOINED) {
9988
+ yield this.publishStream(stream);
9989
+ }
9990
+ this.state.setMediaStream(stream);
9991
+ });
9992
+ }
9993
+ }
9994
+
9995
+ class CameraManager extends InputMediaDeviceManager {
9996
+ constructor(call) {
9997
+ super(call, new CameraManagerState());
9998
+ }
9999
+ /**
10000
+ * Select the camera direaction
10001
+ * @param direction
10002
+ */
10003
+ selectDirection(direction) {
10004
+ return __awaiter(this, void 0, void 0, function* () {
10005
+ this.state.setDirection(direction);
10006
+ // Providing both device id and direction doesn't work, so we deselect the device
10007
+ this.state.setDevice(undefined);
10008
+ yield this.applySettingsToStream();
10009
+ });
10010
+ }
10011
+ /**
10012
+ * Flips the camera direction: if it's front it will change to back, if it's back, it will change to front.
10013
+ *
10014
+ * Note: if there is no available camera with the desired direction, this method will do nothing.
10015
+ * @returns
10016
+ */
10017
+ flip() {
10018
+ return __awaiter(this, void 0, void 0, function* () {
10019
+ const newDirection = this.state.direction === 'front' ? 'back' : 'front';
10020
+ this.selectDirection(newDirection);
10021
+ });
10022
+ }
10023
+ getDevices() {
10024
+ return getVideoDevices();
10025
+ }
10026
+ getStream(constraints) {
10027
+ // We can't set both device id and facing mode
10028
+ // Device id has higher priority
10029
+ if (!constraints.deviceId && this.state.direction) {
10030
+ constraints.facingMode =
10031
+ this.state.direction === 'front' ? 'user' : 'environment';
10032
+ }
10033
+ return getVideoStream(constraints);
10034
+ }
10035
+ publishStream(stream) {
10036
+ return this.call.publishVideoStream(stream);
10037
+ }
10038
+ stopPublishStream() {
10039
+ return this.call.stopPublish(TrackType.VIDEO);
10040
+ }
10041
+ /**
10042
+ * Disables the video tracks of the camera
10043
+ */
10044
+ pause() {
10045
+ var _a;
10046
+ (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks().forEach((track) => {
10047
+ track.enabled = false;
10048
+ });
10049
+ }
10050
+ /**
10051
+ * (Re)enables the video tracks of the camera
10052
+ */
10053
+ resume() {
10054
+ var _a;
10055
+ (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getVideoTracks().forEach((track) => {
10056
+ track.enabled = true;
10057
+ });
10058
+ }
10059
+ }
10060
+
10061
+ class MicrophoneManagerState extends InputMediaDeviceManagerState {
10062
+ getDeviceIdFromStream(stream) {
10063
+ var _a;
10064
+ return (_a = stream.getAudioTracks()[0]) === null || _a === void 0 ? void 0 : _a.getSettings().deviceId;
10065
+ }
10066
+ }
10067
+
10068
+ class MicrophoneManager extends InputMediaDeviceManager {
10069
+ constructor(call) {
10070
+ super(call, new MicrophoneManagerState());
10071
+ }
10072
+ getDevices() {
10073
+ return getAudioDevices();
10074
+ }
10075
+ getStream(constraints) {
10076
+ return getAudioStream(constraints);
10077
+ }
10078
+ publishStream(stream) {
10079
+ return this.call.publishAudioStream(stream);
10080
+ }
10081
+ stopPublishStream() {
10082
+ return this.call.stopPublish(TrackType.AUDIO);
10083
+ }
10084
+ /**
10085
+ * Disables the audio tracks of the microphone
10086
+ */
10087
+ pause() {
10088
+ var _a;
10089
+ (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().forEach((track) => {
10090
+ track.enabled = false;
10091
+ });
10092
+ }
10093
+ /**
10094
+ * (Re)enables the audio tracks of the microphone
10095
+ */
10096
+ resume() {
10097
+ var _a;
10098
+ (_a = this.state.mediaStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().forEach((track) => {
10099
+ track.enabled = true;
10100
+ });
10101
+ }
10102
+ }
10103
+
10104
+ /**
10105
+ * An object representation of a `Call`.
10106
+ */
10107
+ class Call {
10108
+ /**
10109
+ * Constructs a new `Call` instance.
10110
+ *
10111
+ * NOTE: Don't call the constructor directly, instead
10112
+ * Use the [`StreamVideoClient.call`](./StreamVideoClient.md/#call)
10113
+ * method to construct a `Call` instance.
10114
+ */
10115
+ constructor({ type, id, streamClient, members, ownCapabilities, sortParticipantsBy, clientStore, ringing = false, watching = false, }) {
10116
+ /**
10117
+ * ViewportTracker instance
10118
+ */
10119
+ this.viewportTracker = new ViewportTracker();
10120
+ /**
10121
+ * The state of this call.
10122
+ */
10123
+ this.state = new CallState();
10124
+ /**
10125
+ * The permissions context of this call.
10126
+ */
10127
+ this.permissionsContext = new PermissionsContext();
10128
+ /**
10129
+ * The event dispatcher instance dedicated to this Call instance.
10130
+ * @private
10131
+ */
10132
+ this.dispatcher = new Dispatcher();
10133
+ this.trackSubscriptionsSubject = new BehaviorSubject({ type: DebounceType.MEDIUM, data: [] });
10134
+ this.reconnectAttempts = 0;
10135
+ this.maxReconnectAttempts = 10;
10136
+ /**
10137
+ * A list hooks/functions to invoke when the call is left.
10138
+ * A typical use case is to clean up some global event handlers.
10139
+ * @private
10140
+ */
10141
+ this.leaveCallHooks = [];
10142
+ this.streamClientEventHandlers = new Map();
10143
+ /**
10144
+ * Leave the call and stop the media streams that were published by the call.
10145
+ */
10146
+ this.leave = ({ reject = false } = {}) => __awaiter(this, void 0, void 0, function* () {
10147
+ var _a, _b, _c, _d;
10148
+ const callingState = this.state.callingState;
10149
+ if (callingState === CallingState.LEFT) {
10150
+ throw new Error('Cannot leave call that has already been left.');
10151
+ }
10152
+ if (callingState === CallingState.JOINING) {
10153
+ yield this.assertCallJoined();
10154
+ }
10155
+ if (this.ringing) {
10156
+ // I'm the one who started the call, so I should cancel it.
10157
+ const hasOtherParticipants = this.state.remoteParticipants.length > 0;
10158
+ if (this.isCreatedByMe && !hasOtherParticipants) {
10159
+ // Signals other users that I have cancelled my call to them
10160
+ // before they accepted it.
10161
+ yield this.reject();
10162
+ }
10163
+ else if (reject && callingState === CallingState.RINGING) {
10164
+ // Signals other users that I have rejected the incoming call.
10165
+ yield this.reject();
10166
+ }
10167
+ }
10168
+ (_a = this.statsReporter) === null || _a === void 0 ? void 0 : _a.stop();
10169
+ this.statsReporter = undefined;
10170
+ (_b = this.subscriber) === null || _b === void 0 ? void 0 : _b.close();
10171
+ this.subscriber = undefined;
10172
+ (_c = this.publisher) === null || _c === void 0 ? void 0 : _c.close();
10173
+ this.publisher = undefined;
10174
+ (_d = this.sfuClient) === null || _d === void 0 ? void 0 : _d.close();
10175
+ this.sfuClient = undefined;
10176
+ this.dispatcher.offAll();
10177
+ // Call all leave call hooks, e.g. to clean up global event handlers
10178
+ this.leaveCallHooks.forEach((hook) => hook());
10179
+ this.clientStore.unregisterCall(this);
10180
+ this.state.setCallingState(CallingState.LEFT);
10181
+ });
10182
+ /**
10183
+ * Loads the information about the call.
10184
+ *
10185
+ * @param params.ring if set to true, a `call.ring` event will be sent to the call members.
10186
+ * @param params.notify if set to true, a `call.notification` event will be sent to the call members.
10187
+ * @param params.members_limit the members limit.
10188
+ */
10189
+ this.get = (params) => __awaiter(this, void 0, void 0, function* () {
10190
+ const response = yield this.streamClient.get(this.streamClientBasePath, params);
10191
+ if ((params === null || params === void 0 ? void 0 : params.ring) && !this.ringing) {
10192
+ this.ringingSubject.next(true);
10193
+ }
10194
+ this.state.updateFromCallResponse(response.call);
10195
+ this.state.setMembers(response.members);
10196
+ this.state.setOwnCapabilities(response.own_capabilities);
10197
+ if (this.streamClient._hasConnectionID()) {
10198
+ this.watching = true;
10199
+ this.clientStore.registerCall(this);
10200
+ }
10201
+ return response;
10202
+ });
10203
+ /**
10204
+ * Loads the information about the call and creates it if it doesn't exist.
10205
+ *
10206
+ * @param data the data to create the call with.
10207
+ */
10208
+ this.getOrCreate = (data) => __awaiter(this, void 0, void 0, function* () {
10209
+ const response = yield this.streamClient.post(this.streamClientBasePath, data);
10210
+ if ((data === null || data === void 0 ? void 0 : data.ring) && !this.ringing) {
10211
+ this.ringingSubject.next(true);
10212
+ }
10213
+ this.state.updateFromCallResponse(response.call);
10214
+ this.state.setMembers(response.members);
10215
+ this.state.setOwnCapabilities(response.own_capabilities);
10216
+ if (this.streamClient._hasConnectionID()) {
10217
+ this.watching = true;
10218
+ this.clientStore.registerCall(this);
10219
+ }
10220
+ return response;
10221
+ });
10222
+ /**
10223
+ * Creates a call
10224
+ *
10225
+ * @param data the data to create the call with.
10226
+ */
10227
+ this.create = (data) => __awaiter(this, void 0, void 0, function* () {
10228
+ return this.getOrCreate(data);
10229
+ });
10230
+ /**
10231
+ * A shortcut for {@link Call.get} with `ring` parameter set to `true`.
10232
+ * Will send a `call.ring` event to the call members.
10233
+ */
10234
+ this.ring = () => __awaiter(this, void 0, void 0, function* () {
10235
+ return yield this.get({ ring: true });
10236
+ });
10237
+ /**
10238
+ * A shortcut for {@link Call.get} with `notify` parameter set to `true`.
10239
+ * Will send a `call.notification` event to the call members.
10240
+ */
10241
+ this.notify = () => __awaiter(this, void 0, void 0, function* () {
10242
+ return yield this.get({ notify: true });
9656
10243
  });
9657
10244
  /**
9658
10245
  * Marks the incoming call as accepted.
@@ -9680,7 +10267,7 @@ class Call {
9680
10267
  * @returns a promise which resolves once the call join-flow has finished.
9681
10268
  */
9682
10269
  this.join = (data) => __awaiter(this, void 0, void 0, function* () {
9683
- var _e, _f, _g;
10270
+ var _e, _f, _g, _h;
9684
10271
  const callingState = this.state.callingState;
9685
10272
  if ([CallingState.JOINED, CallingState.JOINING].includes(callingState)) {
9686
10273
  this.logger('warn', 'Join method called twice, you should only call this once');
@@ -9949,6 +10536,11 @@ class Call {
9949
10536
  this.state.setServerSidePins(pins);
9950
10537
  this.reconnectAttempts = 0; // reset the reconnect attempts counter
9951
10538
  this.state.setCallingState(CallingState.JOINED);
10539
+ // React uses a different device management for now
10540
+ if (((_h = getSdkInfo()) === null || _h === void 0 ? void 0 : _h.type) !== SdkType.REACT) {
10541
+ this.initCamera();
10542
+ this.initMic();
10543
+ }
9952
10544
  // 3. once we have the "joinResponse", and possibly reconciled the local state
9953
10545
  // we schedule a fast subscription update for all remote participants
9954
10546
  // that were visible before we reconnected or migrated to a new SFU.
@@ -10071,9 +10663,9 @@ class Call {
10071
10663
  * @param trackType the track type to stop publishing.
10072
10664
  */
10073
10665
  this.stopPublish = (trackType) => __awaiter(this, void 0, void 0, function* () {
10074
- var _h;
10666
+ var _j;
10075
10667
  this.logger('info', `stopPublish ${TrackType[trackType]}`);
10076
- yield ((_h = this.publisher) === null || _h === void 0 ? void 0 : _h.unpublishStream(trackType));
10668
+ yield ((_j = this.publisher) === null || _j === void 0 ? void 0 : _j.unpublishStream(trackType));
10077
10669
  });
10078
10670
  /**
10079
10671
  * Update track subscription configuration for one or more participants.
@@ -10174,6 +10766,8 @@ class Call {
10174
10766
  *
10175
10767
  *
10176
10768
  * @param deviceId the selected device, pass `undefined` to clear the device selection
10769
+ *
10770
+ * @deprecated use call.microphone.select
10177
10771
  */
10178
10772
  this.setAudioDevice = (deviceId) => {
10179
10773
  if (!this.sfuClient)
@@ -10188,6 +10782,8 @@ class Call {
10188
10782
  * This method only stores the selection, if you want to start publishing a media stream call the [`publishVideoStream` method](#publishvideostream) that will set `videoDeviceId` as well.
10189
10783
  *
10190
10784
  * @param deviceId the selected device, pass `undefined` to clear the device selection
10785
+ *
10786
+ * @deprecated use call.camera.select
10191
10787
  */
10192
10788
  this.setVideoDevice = (deviceId) => {
10193
10789
  if (!this.sfuClient)
@@ -10220,8 +10816,8 @@ class Call {
10220
10816
  * @returns
10221
10817
  */
10222
10818
  this.updatePublishQuality = (enabledRids) => __awaiter(this, void 0, void 0, function* () {
10223
- var _j;
10224
- return (_j = this.publisher) === null || _j === void 0 ? void 0 : _j.updateVideoPublishQuality(enabledRids);
10819
+ var _k;
10820
+ return (_k = this.publisher) === null || _k === void 0 ? void 0 : _k.updateVideoPublishQuality(enabledRids);
10225
10821
  });
10226
10822
  this.assertCallJoined = () => {
10227
10823
  return new Promise((resolve) => {
@@ -10564,6 +11160,8 @@ class Call {
10564
11160
  this.leaveCallHooks.push(registerEventHandlers(this, this.state, this.dispatcher));
10565
11161
  this.registerEffects();
10566
11162
  this.leaveCallHooks.push(createSubscription(this.trackSubscriptionsSubject.pipe(debounce((v) => timer(v.type)), map$2((v) => v.data)), (subscriptions) => { var _a; return (_a = this.sfuClient) === null || _a === void 0 ? void 0 : _a.updateSubscriptions(subscriptions); }));
11163
+ this.camera = new CameraManager(this);
11164
+ this.microphone = new MicrophoneManager(this);
10567
11165
  }
10568
11166
  registerEffects() {
10569
11167
  this.leaveCallHooks.push(
@@ -10671,6 +11269,64 @@ class Call {
10671
11269
  var _a;
10672
11270
  return ((_a = this.state.createdBy) === null || _a === void 0 ? void 0 : _a.id) === this.currentUserId;
10673
11271
  }
11272
+ initCamera() {
11273
+ var _a, _b, _c;
11274
+ if (((_a = this.state.localParticipant) === null || _a === void 0 ? void 0 : _a.videoStream) ||
11275
+ !this.permissionsContext.hasPermission('send-video')) {
11276
+ return;
11277
+ }
11278
+ // Set camera direction if it's not yet set
11279
+ // This will also start publishing if camera is enabled
11280
+ if (!this.camera.state.direction && !this.camera.state.selectedDevice) {
11281
+ let defaultDirection = 'front';
11282
+ const backendSetting = (_b = this.state.settings) === null || _b === void 0 ? void 0 : _b.video.camera_facing;
11283
+ if (backendSetting) {
11284
+ defaultDirection = backendSetting === 'front' ? 'front' : 'back';
11285
+ }
11286
+ this.camera.selectDirection(defaultDirection);
11287
+ }
11288
+ else if (this.camera.state.status === 'enabled') {
11289
+ // Publish already started media streams (this is the case if there is a lobby screen before join)
11290
+ // Wait for media stream
11291
+ this.camera.state.mediaStream$
11292
+ .pipe(takeWhile((s) => s === undefined, true))
11293
+ .subscribe((stream) => {
11294
+ var _a;
11295
+ if (!((_a = this.state.localParticipant) === null || _a === void 0 ? void 0 : _a.videoStream)) {
11296
+ this.publishVideoStream(stream);
11297
+ }
11298
+ });
11299
+ }
11300
+ // Apply backend config (this is the case if there is no lobby screen before join)
11301
+ if (this.camera.state.status === undefined &&
11302
+ ((_c = this.state.settings) === null || _c === void 0 ? void 0 : _c.video.camera_default_on)) {
11303
+ void this.camera.enable();
11304
+ }
11305
+ }
11306
+ initMic() {
11307
+ var _a, _b;
11308
+ if (((_a = this.state.localParticipant) === null || _a === void 0 ? void 0 : _a.audioStream) ||
11309
+ !this.permissionsContext.hasPermission('send-audio')) {
11310
+ return;
11311
+ }
11312
+ // Publish already started media streams (this is the case if there is a lobby screen before join)
11313
+ if (this.microphone.state.status === 'enabled') {
11314
+ // Wait for media stream
11315
+ this.microphone.state.mediaStream$
11316
+ .pipe(takeWhile((s) => s === undefined, true))
11317
+ .subscribe((stream) => {
11318
+ var _a;
11319
+ if (!((_a = this.state.localParticipant) === null || _a === void 0 ? void 0 : _a.audioStream)) {
11320
+ this.publishAudioStream(stream);
11321
+ }
11322
+ });
11323
+ }
11324
+ // Apply backend config (this is the case if there is no lobby screen before join)
11325
+ if (this.microphone.state.status === undefined &&
11326
+ ((_b = this.state.settings) === null || _b === void 0 ? void 0 : _b.audio.mic_default_on)) {
11327
+ void this.microphone.enable();
11328
+ }
11329
+ }
10674
11330
  }
10675
11331
 
10676
11332
  var https = null;
@@ -11781,7 +12437,7 @@ class WSConnectionFallback {
11781
12437
  }
11782
12438
  }
11783
12439
 
11784
- const version = '0.3.0';
12440
+ const version = '0.3.2';
11785
12441
 
11786
12442
  const logger = getLogger(['location']);
11787
12443
  const HINT_URL = `https://hint.stream-io-video.com/`;
@@ -12755,254 +13411,6 @@ class StreamVideoServerClient extends StreamVideoClient {
12755
13411
  }
12756
13412
  }
12757
13413
 
12758
- const getDevices = (constraints) => {
12759
- return new Observable((subscriber) => {
12760
- navigator.mediaDevices
12761
- .getUserMedia(constraints)
12762
- .then((media) => {
12763
- // in Firefox, devices can be enumerated after userMedia is requested
12764
- // and permissions granted. Otherwise, device labels are empty
12765
- navigator.mediaDevices.enumerateDevices().then((devices) => {
12766
- subscriber.next(devices);
12767
- // If we stop the tracks before enumerateDevices -> the labels won't show up in Firefox
12768
- disposeOfMediaStream(media);
12769
- subscriber.complete();
12770
- });
12771
- })
12772
- .catch((error) => {
12773
- getLogger(['devices'])('error', 'Failed to get devices', error);
12774
- subscriber.error(error);
12775
- });
12776
- });
12777
- };
12778
- /**
12779
- * [Tells if the browser supports audio output change on 'audio' elements](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId).
12780
- *
12781
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12782
- */
12783
- const checkIfAudioOutputChangeSupported = () => {
12784
- if (typeof document === 'undefined')
12785
- return false;
12786
- const element = document.createElement('audio');
12787
- return element.sinkId !== undefined;
12788
- };
12789
- /**
12790
- * The default constraints used to request audio devices.
12791
- */
12792
- const audioDeviceConstraints = {
12793
- audio: {
12794
- autoGainControl: true,
12795
- noiseSuppression: true,
12796
- echoCancellation: true,
12797
- },
12798
- };
12799
- /**
12800
- * The default constraints used to request video devices.
12801
- */
12802
- const videoDeviceConstraints = {
12803
- video: {
12804
- width: 1280,
12805
- height: 720,
12806
- },
12807
- };
12808
- // Audio and video devices are requested in two separate requests: that way users will be presented with two separate prompts -> they can give access to just camera, or just microphone
12809
- const deviceChange$ = new Observable((subscriber) => {
12810
- var _a, _b;
12811
- const deviceChangeHandler = () => subscriber.next();
12812
- (_b = (_a = navigator.mediaDevices).addEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, 'devicechange', deviceChangeHandler);
12813
- return () => {
12814
- var _a, _b;
12815
- return (_b = (_a = navigator.mediaDevices).removeEventListener) === null || _b === void 0 ? void 0 : _b.call(_a, 'devicechange', deviceChangeHandler);
12816
- };
12817
- }).pipe(debounceTime(500), concatMap(() => from(navigator.mediaDevices.enumerateDevices())), shareReplay(1));
12818
- const audioDevices$ = merge(getDevices(audioDeviceConstraints), deviceChange$).pipe(shareReplay(1));
12819
- const videoDevices$ = merge(getDevices(videoDeviceConstraints), deviceChange$).pipe(shareReplay(1));
12820
- /**
12821
- * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audioinput' devices, if devices are added/removed the list is updated.
12822
- *
12823
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12824
- * @returns
12825
- */
12826
- const getAudioDevices = () => audioDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'audioinput')));
12827
- /**
12828
- * Prompts the user for a permission to use video devices (if not already granted) and lists the available 'videoinput' devices, if devices are added/removed the list is updated.
12829
- *
12830
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12831
- * @returns
12832
- */
12833
- const getVideoDevices = () => videoDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'videoinput' && d.deviceId.length)));
12834
- /**
12835
- * Prompts the user for a permission to use audio devices (if not already granted) and lists the available 'audiooutput' devices, if devices are added/removed the list is updated. Selecting 'audiooutput' device only makes sense if [the browser has support for changing audio output on 'audio' elements](#checkifaudiooutputchangesupported)
12836
- *
12837
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12838
- * @returns
12839
- */
12840
- const getAudioOutputDevices = () => {
12841
- return audioDevices$.pipe(map$2((values) => values.filter((d) => d.kind === 'audiooutput')));
12842
- };
12843
- const getStream = (constraints) => __awaiter(void 0, void 0, void 0, function* () {
12844
- try {
12845
- return yield navigator.mediaDevices.getUserMedia(constraints);
12846
- }
12847
- catch (e) {
12848
- getLogger(['devices'])('error', `Failed get user media`, {
12849
- error: e,
12850
- constraints: constraints,
12851
- });
12852
- throw e;
12853
- }
12854
- });
12855
- /**
12856
- * Returns an audio media stream that fulfills the given constraints.
12857
- * If no constraints are provided, it uses the browser's default ones.
12858
- *
12859
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12860
- * @param trackConstraints the constraints to use when requesting the stream.
12861
- * @returns the new `MediaStream` fulfilling the given constraints.
12862
- */
12863
- const getAudioStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, function* () {
12864
- const constraints = {
12865
- audio: Object.assign(Object.assign({}, audioDeviceConstraints.audio), trackConstraints),
12866
- };
12867
- return getStream(constraints);
12868
- });
12869
- /**
12870
- * Returns a video media stream that fulfills the given constraints.
12871
- * If no constraints are provided, it uses the browser's default ones.
12872
- *
12873
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12874
- * @param trackConstraints the constraints to use when requesting the stream.
12875
- * @returns a new `MediaStream` fulfilling the given constraints.
12876
- */
12877
- const getVideoStream = (trackConstraints) => __awaiter(void 0, void 0, void 0, function* () {
12878
- const constraints = {
12879
- video: Object.assign(Object.assign({}, videoDeviceConstraints.video), trackConstraints),
12880
- };
12881
- return getStream(constraints);
12882
- });
12883
- /**
12884
- * Prompts the user for a permission to share a screen.
12885
- * If the user grants the permission, a screen sharing stream is returned. Throws otherwise.
12886
- *
12887
- * The callers of this API are responsible to handle the possible errors.
12888
- *
12889
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12890
- *
12891
- * @param options any additional options to pass to the [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia) API.
12892
- */
12893
- const getScreenShareStream = (options) => __awaiter(void 0, void 0, void 0, function* () {
12894
- try {
12895
- return yield navigator.mediaDevices.getDisplayMedia(Object.assign({ video: true, audio: false }, options));
12896
- }
12897
- catch (e) {
12898
- getLogger(['devices'])('error', 'Failed to get screen share stream', e);
12899
- throw e;
12900
- }
12901
- });
12902
- const watchForDisconnectedDevice = (kind, deviceId$) => {
12903
- let devices$;
12904
- switch (kind) {
12905
- case 'audioinput':
12906
- devices$ = getAudioDevices();
12907
- break;
12908
- case 'videoinput':
12909
- devices$ = getVideoDevices();
12910
- break;
12911
- case 'audiooutput':
12912
- devices$ = getAudioOutputDevices();
12913
- break;
12914
- }
12915
- return combineLatest([devices$, deviceId$]).pipe(filter(([devices, deviceId]) => !!deviceId && !devices.find((d) => d.deviceId === deviceId)), map$2(() => true));
12916
- };
12917
- /**
12918
- * Notifies the subscriber if a given 'audioinput' device is disconnected
12919
- *
12920
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12921
- * @param deviceId$ an Observable that specifies which device to watch for
12922
- * @returns
12923
- */
12924
- const watchForDisconnectedAudioDevice = (deviceId$) => {
12925
- return watchForDisconnectedDevice('audioinput', deviceId$);
12926
- };
12927
- /**
12928
- * Notifies the subscriber if a given 'videoinput' device is disconnected
12929
- *
12930
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12931
- * @param deviceId$ an Observable that specifies which device to watch for
12932
- * @returns
12933
- */
12934
- const watchForDisconnectedVideoDevice = (deviceId$) => {
12935
- return watchForDisconnectedDevice('videoinput', deviceId$);
12936
- };
12937
- /**
12938
- * Notifies the subscriber if a given 'audiooutput' device is disconnected
12939
- *
12940
- * @angular It's recommended to use the [`DeviceManagerService`](./DeviceManagerService.md) for a higher level API, use this low-level method only if the `DeviceManagerService` doesn't suit your requirements.
12941
- * @param deviceId$ an Observable that specifies which device to watch for
12942
- * @returns
12943
- */
12944
- const watchForDisconnectedAudioOutputDevice = (deviceId$) => {
12945
- return watchForDisconnectedDevice('audiooutput', deviceId$);
12946
- };
12947
- const watchForAddedDefaultDevice = (kind) => {
12948
- let devices$;
12949
- switch (kind) {
12950
- case 'audioinput':
12951
- devices$ = getAudioDevices();
12952
- break;
12953
- case 'videoinput':
12954
- devices$ = getVideoDevices();
12955
- break;
12956
- case 'audiooutput':
12957
- devices$ = getAudioOutputDevices();
12958
- break;
12959
- default:
12960
- throw new Error('Unknown MediaDeviceKind', kind);
12961
- }
12962
- return devices$.pipe(pairwise(), filter(([prev, current]) => {
12963
- const prevDefault = prev.find((device) => device.deviceId === 'default');
12964
- const currentDefault = current.find((device) => device.deviceId === 'default');
12965
- return !!(current.length > prev.length &&
12966
- prevDefault &&
12967
- currentDefault &&
12968
- prevDefault.groupId !== currentDefault.groupId);
12969
- }), map$2(() => true));
12970
- };
12971
- /**
12972
- * Notifies the subscriber about newly added default audio input device.
12973
- * @returns Observable<boolean>
12974
- */
12975
- const watchForAddedDefaultAudioDevice = () => watchForAddedDefaultDevice('audioinput');
12976
- /**
12977
- * Notifies the subscriber about newly added default audio output device.
12978
- * @returns Observable<boolean>
12979
- */
12980
- const watchForAddedDefaultAudioOutputDevice = () => watchForAddedDefaultDevice('audiooutput');
12981
- /**
12982
- * Notifies the subscriber about newly added default video input device.
12983
- * @returns Observable<boolean>
12984
- */
12985
- const watchForAddedDefaultVideoDevice = () => watchForAddedDefaultDevice('videoinput');
12986
- /**
12987
- * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
12988
- *
12989
- * @param stream MediaStream
12990
- * @returns void
12991
- */
12992
- const disposeOfMediaStream = (stream) => {
12993
- if (!stream.active)
12994
- return;
12995
- stream.getTracks().forEach((track) => {
12996
- track.stop();
12997
- stream.removeTrack(track);
12998
- });
12999
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
13000
- if (typeof stream.release === 'function') {
13001
- // @ts-expect-error
13002
- stream.release();
13003
- }
13004
- };
13005
-
13006
13414
  const DETECTION_FREQUENCY_IN_MS = 500;
13007
13415
  const AUDIO_LEVEL_THRESHOLD = 150;
13008
13416
  const FFT_SIZE = 128;
@@ -13083,5 +13491,5 @@ var browsers = /*#__PURE__*/Object.freeze({
13083
13491
  isSafari: isSafari
13084
13492
  });
13085
13493
 
13086
- export { AudioSettingsDefaultDeviceEnum, AudioSettingsRequestDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CreateDeviceRequestPushProviderEnum, DebounceType, ErrorFromResponse, OwnCapability, RecordSettingsModeEnum, RecordSettingsQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, events as SfuEvents, models as SfuModels, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoServerClient, StreamVideoWriteableStateStore, TranscriptionSettingsModeEnum, TranscriptionSettingsRequestModeEnum, VideoSettingsCameraFacingEnum, VideoSettingsRequestCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, disposeOfMediaStream, dominantSpeaker, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceInfo, getLogger, getOSInfo, getScreenShareStream, getSdkInfo, getVideoDevices, getVideoStream, isStreamVideoLocalParticipant, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setSdkInfo, speakerLayoutSortPreset, speaking, watchForAddedDefaultAudioDevice, watchForAddedDefaultAudioOutputDevice, watchForAddedDefaultVideoDevice, watchForDisconnectedAudioDevice, watchForDisconnectedAudioOutputDevice, watchForDisconnectedVideoDevice };
13494
+ export { AudioSettingsDefaultDeviceEnum, AudioSettingsRequestDefaultDeviceEnum, browsers as Browsers, Call, CallState, CallType, CallTypes, CallingState, CameraManager, CameraManagerState, CreateDeviceRequestPushProviderEnum, DebounceType, ErrorFromResponse, InputMediaDeviceManager, InputMediaDeviceManagerState, MicrophoneManager, MicrophoneManagerState, OwnCapability, RecordSettingsModeEnum, RecordSettingsQualityEnum, RecordSettingsRequestModeEnum, RecordSettingsRequestQualityEnum, rxUtils as RxUtils, events as SfuEvents, models as SfuModels, StreamSfuClient, StreamVideoClient, StreamVideoReadOnlyStateStore, StreamVideoServerClient, StreamVideoWriteableStateStore, TranscriptionSettingsModeEnum, TranscriptionSettingsRequestModeEnum, VideoSettingsCameraFacingEnum, VideoSettingsRequestCameraFacingEnum, ViewportTracker, VisibilityState, checkIfAudioOutputChangeSupported, combineComparators, conditional, createSoundDetector, defaultSortPreset, descending, disposeOfMediaStream, dominantSpeaker, getAudioDevices, getAudioOutputDevices, getAudioStream, getClientDetails, getDeviceInfo, getLogger, getOSInfo, getScreenShareStream, getSdkInfo, getVideoDevices, getVideoStream, isStreamVideoLocalParticipant, livestreamOrAudioRoomSortPreset, logLevels, logToConsole, name, noopComparator, pinned, publishingAudio, publishingVideo, reactionType, role, screenSharing, setDeviceInfo, setLogLevel, setLogger, setOSInfo, setSdkInfo, speakerLayoutSortPreset, speaking, watchForAddedDefaultAudioDevice, watchForAddedDefaultAudioOutputDevice, watchForAddedDefaultVideoDevice, watchForDisconnectedAudioDevice, watchForDisconnectedAudioOutputDevice, watchForDisconnectedVideoDevice };
13087
13495
  //# sourceMappingURL=index.browser.es.js.map