src/media/ManagedStream.ts
import {ImageCapture} from 'image-capture';
import nanoid from 'nanoid';
import {StreamRecorder} from './StreamRecorder';
/**
* This class provides access to media control functions during a call
* This class must never be directly instantiated
*/
export class ManagedStream {
private mediaStream: MediaStream;
private local: boolean;
private id: string;
/** @ignore */
public constructor(mediaStream: MediaStream, local = false) {
/** @private */
this.mediaStream = mediaStream;
/** @private */
this.local = local;
/** @private */
this.id = nanoid();
}
/**
* @desc The id of this stream
* @return {boolean}
*/
public getId(): string {
return this.id;
}
/**
* @desc Indicates if this managed stream is local. This means that its sources are
* local devices of this machine and that it was obtained of a getUserMedia call
* and not from a RTCPeerConnection
* @return {boolean}
*/
public isLocal(): boolean {
return this.local;
}
/**
* @desc Indicates if this managed stream is remote. This means that its sources are not
* local devices of this machine and that it was obtained from a RTCPeerConnection
* and not from a getUserMedia call
* @return {boolean}
*/
public isRemote(): boolean {
return !this.local;
}
/**
* @desc Provides access to the underlying WebRTC MediaStream object
* @return {MediaStream}
*/
public getMediaStream(): MediaStream {
return this.mediaStream;
}
/**
* @desc Provides access to all the tracks of the managed stream
* @return {MediaStreamTrack[]}
*/
public getTracks(): MediaStreamTrack[] {
return this.mediaStream.getTracks();
}
/**
* @desc Provides access to the audio tracks of the managed stream
* @return {MediaStreamTrack[]}
*/
public getAudioTracks(): MediaStreamTrack[] {
return this.mediaStream.getAudioTracks();
}
/**
* @desc Provides access to the video tracks of the managed stream
* @return {MediaStreamTrack[]}
*/
public getVideoTracks(): MediaStreamTrack[] {
return this.mediaStream.getVideoTracks();
}
/**
* @desc Provides access to the video tracks that are not a screen of the managed stream
* @return {MediaStreamTrack[]}
*/
public getCameraTracks(): MediaStreamTrack[] {
return this.mediaStream.getVideoTracks().filter(track => !track.label.includes('screen'));
}
/**
* @desc Provides access to the screen tracks of the managed stream
* @return {MediaStreamTrack[]}
*/
public getScreenTracks(): MediaStreamTrack[] {
return this.mediaStream.getVideoTracks().filter(
track => ['screen', 'window', 'web-contents-media-stream'].some(p => track.label.startsWith(p + ':')),
);
}
/**
* @desc Allows to know if the managed stream has audio tracks
* @return {boolean} A boolean value indicating if the managed stream has at least one audio track
*/
public hasAudioTracks(): boolean {
return this.getAudioTracks().length > 0;
}
/**
* @desc Allows to know if the managed stream has video tracks
* @return {boolean} A boolean value indicating if the managed stream has at least one video track
*/
public hasVideoTracks(): boolean {
return this.getVideoTracks().length > 0;
}
/**
* @desc Allows to know if the managed stream has camera tracks
* @return {boolean} A boolean value indicating if the managed stream has at least one camera track
*/
public hasCameraTracks(): boolean {
return this.getCameraTracks().length > 0;
}
/**
* @desc Allows to know if the managed stream has screen tracks
* @return {boolean} A boolean value indicating if the managed stream has at least one screen track
*/
public hasScreenTracks(): boolean {
return this.getScreenTracks().length > 0;
}
/**
* @desc Stops every track of the underlying MediaStream
* @return {ManagedStream}
*/
public stop(): void {
this.getTracks().forEach((track) => {
if (track.enabled) {
track.stop();
}
});
}
/**
* @desc Indicates if every audio track is muted
* @return {boolean}
*/
public isAudioMuted(): boolean {
return this.getAudioTracks().every(track => !track.enabled);
}
/**
* @desc Indicates if every video track is muted
* @return {boolean}
*/
public isVideoMuted(): boolean {
return this.getVideoTracks().every(track => !track.enabled);
}
/**
* @desc Indicates if every screen track is muted
* @return {boolean}
*/
public isScreenMuted(): boolean {
return this.getScreenTracks().every(track => !track.enabled);
}
/**
* @desc Mutes or unmutes audio tracks of this stream
* @param {boolean} [mute] If true disables audio tracks, if false enables them
* If not argument is provided it toggles the mute state on audio tracks
*/
public muteAudio(mute = !this.isAudioMuted()): void {
this.getAudioTracks().forEach(track => track.enabled = !mute);
}
/**
* @desc Mutes or unmutes video tracks of this stream
* @param {boolean} [mute] If true disables video tracks, if false enables them
* If not argument is provided it toggles the mute state on video tracks
*/
public muteVideo(mute = !this.isVideoMuted()): void {
this.getVideoTracks().forEach(track => track.enabled = !mute);
}
/**
* @desc Mutes or unmutes screen tracks of this stream
* @param {boolean} [mute] If true disables screen tracks, if false enables them
* If not argument is provided it toggles the mute state on screen tracks
*/
public muteScreen(mute = !this.isScreenMuted()): void {
this.getScreenTracks().forEach(track => track.enabled = !mute);
}
/**
* @desc Allows taking captures of the video tracks of this stream
* @return {Promise<Blob[]>} A promise containing an array of Blobs with a capture
* of the video. One Blob for each video track of this stream
*/
public takePhotos(): Promise<Blob[]> {
const photos = this.mediaStream.getVideoTracks()
.map(track => new ImageCapture(track))
.map(captureDevice => (captureDevice.takePhoto() as Promise<Blob>));
return Promise.all(photos);
}
/**
* @desc Creates a StreamRecorder object that will allow recording the stream
* @return {StreamRecorder}
*/
public createStreamRecorder(): StreamRecorder {
return new StreamRecorder(this.mediaStream);
}
}