Skip to content

Commit 9dfbc55

Browse files
authored
fix: make camera.flip() work more reliably with older devices (#1781)
While optimizing the `camera.flip()` API in #1679, we introduced a regression that led to `camera.flip()` throwing a `NotReadableError` on devices with limited hardware capabilities. Some devices are unable to provide two `MediaStream` instances at the same time. ### 📝 Implementation notes We want to preserve the `camera.flip()` optimizations as they allow significantly faster direction change. We always attempt to perform a fast direction change, but if it fails, we fall back to: `stop()` -> `flip()` -> `start()` flow, which is slower, but it works. 🎫 Ticket: https://linear.app/stream/issue/REACT-367/
1 parent c686451 commit 9dfbc55

File tree

1 file changed

+26
-22
lines changed

1 file changed

+26
-22
lines changed

packages/client/src/devices/CameraManager.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,34 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
3333
* @param direction the direction of the camera to select.
3434
*/
3535
async selectDirection(direction: Exclude<CameraDirection, undefined>) {
36-
if (this.isDirectionSupportedByDevice()) {
37-
if (isReactNative()) {
38-
const videoTrack = this.getTracks()[0];
39-
if (!videoTrack) {
40-
this.logger('warn', 'No video track found to do direction selection');
41-
return;
42-
}
43-
await videoTrack.applyConstraints({
44-
facingMode: direction === 'front' ? 'user' : 'environment',
45-
});
46-
this.state.setDirection(direction);
47-
this.state.setDevice(undefined);
48-
} else {
49-
// web mobile
50-
this.state.setDirection(direction);
51-
// Providing both device id and direction doesn't work, so we deselect the device
52-
this.state.setDevice(undefined);
53-
this.getTracks().forEach((track) => {
54-
track.stop();
55-
});
36+
if (!this.isDirectionSupportedByDevice()) {
37+
this.logger('warn', 'Setting direction is not supported on this device');
38+
return;
39+
}
40+
41+
// providing both device id and direction doesn't work, so we deselect the device
42+
this.state.setDirection(direction);
43+
this.state.setDevice(undefined);
44+
45+
if (isReactNative()) {
46+
const videoTrack = this.getTracks()[0] as MediaStreamTrack | undefined;
47+
await videoTrack?.applyConstraints({
48+
facingMode: direction === 'front' ? 'user' : 'environment',
49+
});
50+
return;
51+
}
52+
53+
this.getTracks().forEach((track) => track.stop());
54+
try {
55+
await this.unmuteStream();
56+
} catch (error) {
57+
if (error instanceof Error && error.name === 'NotReadableError') {
58+
// the camera is already in use, and the device can't use it unless it's released.
59+
// in that case, we need to stop the stream and start it again.
60+
await this.muteStream();
5661
await this.unmuteStream();
5762
}
58-
} else {
59-
this.logger('warn', 'Camera direction ignored for desktop devices');
63+
throw error;
6064
}
6165
}
6266

0 commit comments

Comments
 (0)