import { BehaviorSubject, distinctUntilChanged } from 'rxjs'; import { DeviceManagerState } from './DeviceManagerState'; import { isReactNative } from '../helpers/platforms'; import { getVideoBrowserPermission } from './devices'; import { RxUtils } from '../store'; import { Tracer } from '../stats'; export type CameraDirection = 'front' | 'back' | undefined; export class CameraManagerState extends DeviceManagerState { private directionSubject = new BehaviorSubject(undefined); /** * Observable that emits the preferred camera direction * front - means the camera facing the user * back - means the camera facing the environment */ direction$ = this.directionSubject .asObservable() .pipe(distinctUntilChanged()); constructor(tracer: Tracer | undefined) { super('stop-tracks', getVideoBrowserPermission(tracer)); } /** * The preferred camera direction * front - means the camera facing the user * back - means the camera facing the environment */ get direction() { return RxUtils.getCurrentValue(this.direction$); } /** * @internal */ setDirection(direction: CameraDirection) { RxUtils.setCurrentValue(this.directionSubject, direction); } /** * @internal */ override setMediaStream( stream: MediaStream | undefined, rootStream: MediaStream | undefined, ): void { super.setMediaStream(stream, rootStream); if (stream) { // RN getSettings() doesn't return facingMode, so we don't verify camera direction const direction = isReactNative() ? this.direction : stream.getVideoTracks()[0]?.getSettings().facingMode === 'environment' ? 'back' : 'front'; this.setDirection(direction); } } protected override getDeviceIdFromStream( stream: MediaStream, ): string | undefined { const [track] = stream.getVideoTracks(); return track?.getSettings().deviceId; } }