Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into codec-negotiation
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverlaz committed Oct 31, 2024
2 parents 446d63f + 6a41d14 commit f5a4bb7
Show file tree
Hide file tree
Showing 26 changed files with 531 additions and 107 deletions.
21 changes: 21 additions & 0 deletions packages/client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).

## [1.10.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.10.0...@stream-io/video-client-1.10.1) (2024-10-30)


### Bug Fixes

* various device selector issues ([#1541](https://github.com/GetStream/stream-video-js/issues/1541)) ([f23618b](https://github.com/GetStream/stream-video-js/commit/f23618bda447eeb2d66f908bdb38b24db051f87c))

## [1.10.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.9.3...@stream-io/video-client-1.10.0) (2024-10-30)


### Features

* report input devices in call stats ([#1533](https://github.com/GetStream/stream-video-js/issues/1533)) ([f34fe0a](https://github.com/GetStream/stream-video-js/commit/f34fe0a0444903099565ae55a9639e39fc19b76c))

## [1.9.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.9.2...@stream-io/video-client-1.9.3) (2024-10-28)


### Bug Fixes

* make device selection by device id exact ([#1538](https://github.com/GetStream/stream-video-js/issues/1538)) ([6274cac](https://github.com/GetStream/stream-video-js/commit/6274cac2ecf155aa6ce0c6d764229e0e9cd39a6a))

## [1.9.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.9.1...@stream-io/video-client-1.9.2) (2024-10-21)


Expand Down
2 changes: 1 addition & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stream-io/video-client",
"version": "1.9.2",
"version": "1.10.1",
"packageManager": "[email protected]",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
Expand Down
3 changes: 3 additions & 0 deletions packages/client/src/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,9 @@ export class Call {
options: statsOptions,
subscriber: this.subscriber,
publisher: this.publisher,
microphone: this.microphone,
camera: this.camera,
state: this.state,
});
this.sfuStatsReporter.start();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/coordinator/connection/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export class StreamClient {
) {
this.logger(
'warn',
'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.',
'Please do not use connectUser server side. Use our @stream-io/node-sdk instead: https://getstream.io/video/docs/api/',
);
}

Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/devices/BrowserPermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class BrowserPermission {

this.ready = (async () => {
const assumeGranted = (error?: unknown) => {
this.setState('granted');
this.setState('prompt');
};

if (!canQueryPermissions()) {
Expand Down Expand Up @@ -88,12 +88,14 @@ export class BrowserPermission {
this.permission.constraints,
);
disposeOfMediaStream(stream);
this.setState('granted');
return true;
} catch (e) {
if (e instanceof DOMException && e.name === 'NotAllowedError') {
this.logger('info', 'Browser permission was not granted', {
permission: this.permission,
});
this.setState('denied');

if (throwOnNotAllowed) {
throw e;
Expand Down
16 changes: 12 additions & 4 deletions packages/client/src/devices/InputMediaDeviceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,17 @@ export abstract class InputMediaDeviceManager<
'This method is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for reference.',
);
}
if (deviceId === this.state.selectedDevice) {
const prevDeviceId = this.state.selectedDevice;
if (deviceId === prevDeviceId) {
return;
}
this.state.setDevice(deviceId);
await this.applySettingsToStream();
try {
this.state.setDevice(deviceId);
await this.applySettingsToStream();
} catch (error) {
this.state.setDevice(prevDeviceId);
throw error;
}
}

/**
Expand Down Expand Up @@ -308,7 +314,9 @@ export abstract class InputMediaDeviceManager<
const defaultConstraints = this.state.defaultConstraints;
const constraints: MediaTrackConstraints = {
...defaultConstraints,
deviceId: this.state.selectedDevice,
deviceId: this.state.selectedDevice
? { exact: this.state.selectedDevice }
: undefined,
};

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/devices/__tests__/CameraManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe('CameraManager', () => {
await manager.select(deviceId);

expect((getVideoStream as Mock).mock.lastCall[0]).toEqual({
deviceId,
deviceId: { exact: deviceId },
width: 1280,
height: 720,
});
Expand All @@ -182,7 +182,7 @@ describe('CameraManager', () => {
await manager.selectTargetResolution({ width: 640, height: 480 });

expect((getVideoStream as Mock).mock.lastCall[0]).toEqual({
deviceId: mockVideoDevices[0].deviceId,
deviceId: { exact: mockVideoDevices[0].deviceId },
width: 640,
height: 480,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ describe('InputMediaDeviceManager.test', () => {

expect(manager.stopPublishStream).toHaveBeenCalledWith(true);
expect(manager.getStream).toHaveBeenCalledWith({
deviceId,
deviceId: { exact: deviceId },
});
expect(manager.publishStream).toHaveBeenCalled();
});
Expand Down
69 changes: 51 additions & 18 deletions packages/client/src/devices/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { getLogger } from '../logger';
import { BrowserPermission } from './BrowserPermission';
import { lazy } from '../helpers/lazy';
import { isFirefox } from '../helpers/browsers';

/**
* Returns an Observable that emits the list of available devices
Expand All @@ -32,7 +33,12 @@ const getDevices = (permission: BrowserPermission, kind: MediaDeviceKind) => {
await permission.prompt({ throwOnNotAllowed: true });
devices = await navigator.mediaDevices.enumerateDevices();
}
return devices.filter((d) => d.kind === kind);
return devices.filter(
(device) =>
device.kind === kind &&
device.label !== '' &&
device.deviceId !== 'default',
);
})(),
);
};
Expand Down Expand Up @@ -125,7 +131,7 @@ export const getAudioDevices = lazy(() => {
* if devices are added/removed the list is updated, and if the permission is revoked,
* the observable errors.
*/
export const getVideoDevices = () => {
export const getVideoDevices = lazy(() => {
return merge(
getDeviceChangeObserver(),
getVideoBrowserPermission().asObservable(),
Expand All @@ -134,15 +140,15 @@ export const getVideoDevices = () => {
concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput')),
shareReplay(1),
);
};
});

/**
* Prompts the user for a permission to use video devices (if not already granted
* and was not prompted before) and lists the available 'audiooutput' devices,
* if devices are added/removed the list is updated, and if the permission is revoked,
* the observable errors.
*/
export const getAudioOutputDevices = () => {
export const getAudioOutputDevices = lazy(() => {
return merge(
getDeviceChangeObserver(),
getAudioBrowserPermission().asObservable(),
Expand All @@ -151,10 +157,17 @@ export const getAudioOutputDevices = () => {
concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput')),
shareReplay(1),
);
};
});

const getStream = async (constraints: MediaStreamConstraints) => {
return await navigator.mediaDevices.getUserMedia(constraints);
const stream = await navigator.mediaDevices.getUserMedia(constraints);
if (isFirefox()) {
// When enumerating devices, Firefox will hide device labels unless there's been
// an active user media stream on the page. So we force device list updates after
// every successful getUserMedia call.
navigator.mediaDevices.dispatchEvent(new Event('devicechange'));
}
return stream;
};

/**
Expand All @@ -167,7 +180,7 @@ const getStream = async (constraints: MediaStreamConstraints) => {
*/
export const getAudioStream = async (
trackConstraints?: MediaTrackConstraints,
) => {
): Promise<MediaStream> => {
const constraints: MediaStreamConstraints = {
audio: {
...audioDeviceConstraints.audio,
Expand All @@ -180,13 +193,23 @@ export const getAudioStream = async (
throwOnNotAllowed: true,
forcePrompt: true,
});
return getStream(constraints);
} catch (e) {
return await getStream(constraints);
} catch (error) {
if (error instanceof OverconstrainedError && trackConstraints?.deviceId) {
const { deviceId, ...relaxedContraints } = trackConstraints;
getLogger(['devices'])(
'warn',
'Failed to get audio stream, will try again with relaxed contraints',
{ error, constraints, relaxedContraints },
);
return getAudioStream(relaxedContraints);
}

getLogger(['devices'])('error', 'Failed to get audio stream', {
error: e,
constraints: constraints,
error,
constraints,
});
throw e;
throw error;
}
};

Expand All @@ -200,7 +223,7 @@ export const getAudioStream = async (
*/
export const getVideoStream = async (
trackConstraints?: MediaTrackConstraints,
) => {
): Promise<MediaStream> => {
const constraints: MediaStreamConstraints = {
video: {
...videoDeviceConstraints.video,
Expand All @@ -212,13 +235,23 @@ export const getVideoStream = async (
throwOnNotAllowed: true,
forcePrompt: true,
});
return getStream(constraints);
} catch (e) {
return await getStream(constraints);
} catch (error) {
if (error instanceof OverconstrainedError && trackConstraints?.deviceId) {
const { deviceId, ...relaxedContraints } = trackConstraints;
getLogger(['devices'])(
'warn',
'Failed to get video stream, will try again with relaxed contraints',
{ error, constraints, relaxedContraints },
);
return getVideoStream(relaxedContraints);
}

getLogger(['devices'])('error', 'Failed to get video stream', {
error: e,
constraints: constraints,
error,
constraints,
});
throw e;
throw error;
}
};

Expand Down
Loading

0 comments on commit f5a4bb7

Please sign in to comment.