@stream-io/video-client 1.8.2 → 1.8.4

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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Checks if the current platform is a mobile device.
3
+ *
4
+ * See:
5
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
6
+ */
7
+ export declare const isMobile: () => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Checks if the current platform is a mobile device.
3
+ *
4
+ * See:
5
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
6
+ */
7
+ export const isMobile = () => /Mobi/i.test(navigator.userAgent);
@@ -5,6 +5,7 @@ import { InputMediaDeviceManager } from './InputMediaDeviceManager';
5
5
  import { getVideoDevices, getVideoStream } from './devices';
6
6
  import { TrackType } from '../gen/video/sfu/models/models';
7
7
  import { PreferredCodec, PublishOptions } from '../types';
8
+ import { isMobile } from '../compatibility';
8
9
 
9
10
  export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
10
11
  private targetResolution = {
@@ -34,10 +35,14 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
34
35
  * @param direction the direction of the camera to select.
35
36
  */
36
37
  async selectDirection(direction: Exclude<CameraDirection, undefined>) {
37
- this.state.setDirection(direction);
38
- // Providing both device id and direction doesn't work, so we deselect the device
39
- this.state.setDevice(undefined);
40
- await this.applySettingsToStream();
38
+ if (isMobile()) {
39
+ this.state.setDirection(direction);
40
+ // Providing both device id and direction doesn't work, so we deselect the device
41
+ this.state.setDevice(undefined);
42
+ await this.applySettingsToStream();
43
+ } else {
44
+ this.logger('warn', 'Camera direction ignored for desktop devices');
45
+ }
41
46
  }
42
47
 
43
48
  /**
@@ -131,7 +136,7 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
131
136
  constraints.height = this.targetResolution.height;
132
137
  // We can't set both device id and facing mode
133
138
  // Device id has higher priority
134
- if (!constraints.deviceId && this.state.direction) {
139
+ if (!constraints.deviceId && this.state.direction && isMobile()) {
135
140
  constraints.facingMode =
136
141
  this.state.direction === 'front' ? 'user' : 'environment';
137
142
  }
@@ -279,14 +279,7 @@ export abstract class InputMediaDeviceManager<
279
279
 
280
280
  private stopTracks() {
281
281
  this.getTracks().forEach((track) => {
282
- if (track.readyState === 'live') {
283
- track.stop();
284
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
285
- if (typeof track.release === 'function') {
286
- // @ts-expect-error
287
- track.release();
288
- }
289
- }
282
+ if (track.readyState === 'live') track.stop();
290
283
  });
291
284
  }
292
285
 
@@ -36,6 +36,13 @@ vi.mock('../../Call.ts', () => {
36
36
  };
37
37
  });
38
38
 
39
+ vi.mock('../../compatibility.ts', () => {
40
+ console.log('MOCKING mobile device');
41
+ return {
42
+ isMobile: () => true,
43
+ };
44
+ });
45
+
39
46
  describe('CameraManager', () => {
40
47
  let manager: CameraManager;
41
48
 
@@ -276,12 +276,6 @@ export const disposeOfMediaStream = (stream: MediaStream) => {
276
276
  if (!stream.active) return;
277
277
  stream.getTracks().forEach((track) => {
278
278
  track.stop();
279
- stream.removeTrack(track);
280
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
281
- if (typeof track.release === 'function') {
282
- // @ts-expect-error
283
- track.release();
284
- }
285
279
  });
286
280
  // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
287
281
  if (typeof stream.release === 'function') {
@@ -106,14 +106,7 @@ export class RNSpeechDetector {
106
106
  if (!this.audioStream) {
107
107
  return;
108
108
  }
109
- this.audioStream.getTracks().forEach((track) => {
110
- track.stop();
111
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
112
- if (typeof track.release === 'function') {
113
- // @ts-expect-error
114
- track.release();
115
- }
116
- });
109
+ this.audioStream.getTracks().forEach((track) => track.stop());
117
110
  if (
118
111
  // @ts-expect-error release() is present in react-native-webrtc
119
112
  typeof this.audioStream.release === 'function'
@@ -316,11 +316,6 @@ export class Publisher {
316
316
  if (previousTrack && previousTrack !== track) {
317
317
  previousTrack.stop();
318
318
  previousTrack.removeEventListener('ended', handleTrackEnded);
319
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
320
- if (typeof previousTrack.release === 'function') {
321
- // @ts-expect-error
322
- track.release();
323
- }
324
319
  track.addEventListener('ended', handleTrackEnded);
325
320
  }
326
321
  if (!track.enabled) {
@@ -342,18 +337,16 @@ export class Publisher {
342
337
  const transceiver = this.pc
343
338
  .getTransceivers()
344
339
  .find((t) => t === this.transceiverRegistry[trackType] && t.sender.track);
345
- const track = transceiver?.sender.track;
346
- if (track && (stopTrack ? track.readyState === 'live' : track.enabled)) {
347
- if (stopTrack) {
348
- track.stop();
349
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
350
- if (typeof track.release === 'function') {
351
- // @ts-expect-error
352
- track.release();
353
- }
354
- } else {
355
- track.enabled = false;
356
- }
340
+ if (
341
+ transceiver &&
342
+ transceiver.sender.track &&
343
+ (stopTrack
344
+ ? transceiver.sender.track.readyState === 'live'
345
+ : transceiver.sender.track.enabled)
346
+ ) {
347
+ stopTrack
348
+ ? transceiver.sender.track.stop()
349
+ : (transceiver.sender.track.enabled = false);
357
350
  // We don't need to notify SFU if unpublishing in response to remote soft mute
358
351
  if (this.state.localParticipant?.publishedTracks.includes(trackType)) {
359
352
  await this.notifyTrackMuteStateChanged(undefined, trackType, true);
@@ -406,13 +399,7 @@ export class Publisher {
406
399
  private stopPublishing = () => {
407
400
  this.logger('debug', 'Stopping publishing all tracks');
408
401
  this.pc.getSenders().forEach((s) => {
409
- const track = s.track;
410
- track?.stop();
411
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the track
412
- if (typeof track?.release === 'function') {
413
- // @ts-expect-error
414
- track.release();
415
- }
402
+ s.track?.stop();
416
403
  if (this.pc.signalingState !== 'closed') {
417
404
  this.pc.removeTrack(s);
418
405
  }