import { Observable, of } from 'rxjs'; import { AudioDeviceManager, createAudioConstraints, } from './AudioDeviceManager'; import { ScreenShareState } from './ScreenShareState'; import { Call } from '../Call'; import { AudioBitrateProfile, TrackType } from '../gen/video/sfu/models/models'; import { getScreenShareStream } from './devices'; import { ScreenShareSettings } from '../types'; import { createSubscription } from '../store/rxUtils'; import { normalize } from './devicePersistence'; export class ScreenShareManager extends AudioDeviceManager< ScreenShareState, DisplayMediaStreamOptions > { constructor(call: Call) { super( call, new ScreenShareState(), TrackType.SCREEN_SHARE, normalize({ enabled: false }), ); } override setup(): void { if (this.areSubscriptionsSetUp) return; super.setup(); this.subscriptions.push( createSubscription(this.call.state.settings$, (settings) => { const maybeTargetResolution = settings?.screensharing.target_resolution; if (maybeTargetResolution) { this.setDefaultConstraints({ ...this.state.defaultConstraints, video: { width: maybeTargetResolution.width, height: maybeTargetResolution.height, }, }); } }), ); } /** * Will enable screen share audio options on supported platforms. * * Note: for ongoing screen share, audio won't be enabled until you * re-publish the screen share stream. */ enableScreenShareAudio(): void { this.state.setAudioEnabled(true); } /** * Will disable screen share audio options on supported platforms. */ async disableScreenShareAudio(): Promise { this.state.setAudioEnabled(false); if (this.call.publisher?.isPublishing(TrackType.SCREEN_SHARE_AUDIO)) { await this.call.stopPublish(TrackType.SCREEN_SHARE_AUDIO); } } /** * Returns the current screen share settings. */ getSettings(): ScreenShareSettings | undefined { return this.state.settings; } /** * Sets the current screen share settings. * * @param settings the settings to set. */ setSettings(settings: ScreenShareSettings | undefined): void { this.state.setSettings(settings); } protected getDevices(): Observable { return of([]); // there are no devices to be listed for Screen Share } protected override async getStream( constraints: DisplayMediaStreamOptions, ): Promise { if (!this.state.audioEnabled) { constraints.audio = false; } const stream = await getScreenShareStream(constraints, this.call.tracer); const [track] = stream.getVideoTracks(); const { contentHint } = this.state.settings || {}; if (typeof contentHint !== 'undefined' && track && 'contentHint' in track) { this.call.tracer.trace( 'navigator.mediaDevices.getDisplayMedia.contentHint', contentHint, ); track.contentHint = contentHint; } return stream; } protected override doSetAudioBitrateProfile(profile: AudioBitrateProfile) { const { defaultConstraints } = this.state; const baseAudioConstraints = typeof defaultConstraints?.audio !== 'boolean' ? defaultConstraints?.audio : null; this.setDefaultConstraints({ ...defaultConstraints, audio: { ...baseAudioConstraints, ...createAudioConstraints(profile), }, }); } protected override async stopPublishStream(): Promise { return this.call.stopPublish( TrackType.SCREEN_SHARE, TrackType.SCREEN_SHARE_AUDIO, ); } /** * Overrides the default `select` method to throw an error. */ override async select(): Promise { throw new Error('Not supported'); } }