diff --git a/modules/RTC/JitsiTrack.js b/modules/RTC/JitsiTrack.ts similarity index 81% rename from modules/RTC/JitsiTrack.js rename to modules/RTC/JitsiTrack.ts index 44ca02f819..0a58fb36f6 100644 --- a/modules/RTC/JitsiTrack.js +++ b/modules/RTC/JitsiTrack.ts @@ -1,11 +1,12 @@ import { getLogger } from '@jitsi/logger'; - +import EventEmitter from "events"; +import JitsiConference from "../../JitsiConference"; +import { MediaType } from "../../service/RTC/MediaType"; +import { VideoType } from "../../service/RTC/VideoType"; +import TraceablePeerConnection from "./TraceablePeerConnection"; +import RTCUtils from './RTCUtils'; import * as JitsiTrackEvents from '../../JitsiTrackEvents'; -import { MediaType } from '../../service/RTC/MediaType'; import browser from '../browser'; -import EventEmitter from '../util/EventEmitter'; - -import RTCUtils from './RTCUtils'; const logger = getLogger(__filename); @@ -22,6 +23,17 @@ const trackHandler2Prop = { * Represents a single media track (either audio or video). */ export default class JitsiTrack extends EventEmitter { + public conference: JitsiConference | null; + public audioLevel: number; + public containers: HTMLElement[]; + public handlers: Map; + public type: MediaType; + public disposed: boolean; + private stream: MediaStream; + private track: MediaStreamTrack; + private videoType: VideoType; + private _streamInactiveHandler: Function | null; + /* eslint-disable max-params */ /** * Represents a single media track (either audio or video). @@ -36,12 +48,12 @@ export default class JitsiTrack extends EventEmitter { * @param videoType the VideoType for this track if any */ constructor( - conference, - stream, - track, - streamInactiveHandler, - trackMediaType, - videoType) { + conference: JitsiConference | null, + stream: MediaStream, + track: MediaStreamTrack, + streamInactiveHandler: Function, + trackMediaType: MediaType, + videoType: VideoType) { super(); /** @@ -83,11 +95,13 @@ export default class JitsiTrack extends EventEmitter { * a onended event on the MediaStreamTrack. * @param {Function} handler the handler */ - _addMediaStreamInactiveHandler(handler) { + _addMediaStreamInactiveHandler(handler: Function): void { if (browser.isFirefox() || browser.isWebKitBased()) { - this.track.onended = handler; + this.track.onended = handler as (this: MediaStreamTrack, ev: Event) => any; } else { - this.stream.oninactive = handler; + this.stream.getTracks().forEach(track => { + track.onended = handler as (this: MediaStreamTrack, ev: Event) => any; + }); } } @@ -99,7 +113,7 @@ export default class JitsiTrack extends EventEmitter { * 'audio' element. * @private */ - _attachTTFMTracker(container) { // eslint-disable-line no-unused-vars + _attachTTFMTracker(container: HTMLElement): void { // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } @@ -110,7 +124,7 @@ export default class JitsiTrack extends EventEmitter { * 'audio' element. * @private */ - _onTrackAttach(container) { // eslint-disable-line no-unused-vars + _onTrackAttach(container: HTMLElement): void { // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } @@ -121,7 +135,7 @@ export default class JitsiTrack extends EventEmitter { * 'audio' element. * @private */ - _onTrackDetach(container) { // eslint-disable-line no-unused-vars + _onTrackDetach(container: HTMLElement): void { // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } @@ -131,7 +145,7 @@ export default class JitsiTrack extends EventEmitter { * @param {string} type the type of the handler that is going to be set * @param {Function} handler the handler. */ - _setHandler(type, handler) { + _setHandler(type: string, handler: Function): void { if (!trackHandler2Prop.hasOwnProperty(type)) { logger.error(`Invalid handler type ${type}`); @@ -157,7 +171,7 @@ export default class JitsiTrack extends EventEmitter { * @param {MediaStream} stream the new stream. * @protected */ - _setStream(stream) { + _setStream(stream: MediaStream): void { if (this.stream === stream) { return; } @@ -182,7 +196,7 @@ export default class JitsiTrack extends EventEmitter { * Unregisters all event handlers bound to the underlying media stream/track * @private */ - _unregisterHandlers() { + _unregisterHandlers(): void { if (!this.stream) { logger.warn( `${this}: unable to unregister handlers - no stream object`); @@ -211,7 +225,7 @@ export default class JitsiTrack extends EventEmitter { * * @returns {void} */ - attach(container) { + attach(container: HTMLElement): Promise { let result = Promise.resolve(); if (this.stream) { @@ -232,7 +246,7 @@ export default class JitsiTrack extends EventEmitter { * container can be a 'video', 'audio' or 'object' HTML element instance to * which this JitsiTrack is currently attached. */ - detach(container) { + detach(container?: HTMLElement): void { for (let cs = this.containers, i = cs.length - 1; i >= 0; --i) { const c = cs[i]; @@ -260,7 +274,7 @@ export default class JitsiTrack extends EventEmitter { * * @returns {Promise} */ - dispose() { + dispose(): Promise { const p = Promise.resolve(); if (this.disposed) { @@ -279,14 +293,14 @@ export default class JitsiTrack extends EventEmitter { * Returns id of the track. * @returns {string|null} id of the track or null if this is fake track. */ - getId() { + getId(): string | null { return this.getStreamId(); } /** * Returns the WebRTC MediaStream instance. */ - getOriginalStream() { + getOriginalStream(): MediaStream { return this.stream; } @@ -294,7 +308,7 @@ export default class JitsiTrack extends EventEmitter { * Returns the source name of the track. * @returns {String|undefined} */ - getSourceName() { // eslint-disable-line no-unused-vars + getSourceName(){ // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } @@ -302,7 +316,7 @@ export default class JitsiTrack extends EventEmitter { * Returns the primary SSRC associated with the track. * @returns {number} */ - getSsrc() { // eslint-disable-line no-unused-vars + getSsrc(){ // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } @@ -310,7 +324,7 @@ export default class JitsiTrack extends EventEmitter { * Returns the ID of the underlying WebRTC Media Stream(if any) * @returns {String|null} */ - getStreamId() { + getStreamId(): string | null { return this.stream ? this.stream.id : null; } @@ -318,7 +332,7 @@ export default class JitsiTrack extends EventEmitter { * Return the underlying WebRTC MediaStreamTrack * @returns {MediaStreamTrack} */ - getTrack() { + getTrack(): MediaStreamTrack { return this.track; } @@ -326,7 +340,7 @@ export default class JitsiTrack extends EventEmitter { * Return the underlying WebRTC MediaStreamTrack label * @returns {string} */ - getTrackLabel() { + getTrackLabel(): string { return this.track.label; } @@ -334,14 +348,14 @@ export default class JitsiTrack extends EventEmitter { * Returns the ID of the underlying WebRTC MediaStreamTrack(if any) * @returns {String|null} */ - getTrackId() { + getTrackId(): string | null { return this.track ? this.track.id : null; } /** * Returns the type (audio or video) of this track. */ - getType() { + getType(): MediaType { return this.type; } @@ -350,7 +364,7 @@ export default class JitsiTrack extends EventEmitter { * eventual video type. * @returns {string} */ - getUsageLabel() { + getUsageLabel(): string { if (this.isAudioTrack()) { return 'mic'; } @@ -361,21 +375,21 @@ export default class JitsiTrack extends EventEmitter { /** * Returns the video type (camera or desktop) of this track. */ - getVideoType() { + getVideoType(): VideoType { return this.videoType; } /** * Returns the height of the track in normalized landscape format. */ - getHeight() { + getHeight(): number { return Math.min(this.track.getSettings().height, this.track.getSettings().width); } /** * Returns the width of the track in normalized landscape format. */ - getWidth() { + getWidth(): number { return Math.max(this.track.getSettings().height, this.track.getSettings().width); } @@ -385,7 +399,7 @@ export default class JitsiTrack extends EventEmitter { * will return that stream is active (in case of FF). * @returns {boolean} whether MediaStream is active. */ - isActive() { + isActive(): boolean { if (typeof this.stream.active !== 'undefined') { return this.stream.active; } @@ -396,7 +410,7 @@ export default class JitsiTrack extends EventEmitter { /** * Check if this is an audio track. */ - isAudioTrack() { + isAudioTrack(): boolean { return this.getType() === MediaType.AUDIO; } @@ -405,7 +419,7 @@ export default class JitsiTrack extends EventEmitter { * @abstract * @return {boolean} 'true' if it's a local track or 'false' otherwise. */ - isLocal() { + isLocal(): boolean { throw new Error('Not implemented by subclass'); } @@ -414,14 +428,14 @@ export default class JitsiTrack extends EventEmitter { * * @return {boolean} - true if track represents a local audio track, false otherwise. */ - isLocalAudioTrack() { + isLocalAudioTrack(): boolean { return this.isAudioTrack() && this.isLocal(); } /** * Check if this is a video track. */ - isVideoTrack() { + isVideoTrack(): boolean { return this.getType() === MediaType.VIDEO; } @@ -431,7 +445,7 @@ export default class JitsiTrack extends EventEmitter { * @return {boolean} true if the underlying * MediaStreamTrack is muted or false otherwise. */ - isWebRTCTrackMuted() { + isWebRTCTrackMuted(): boolean { return this.track && this.track.muted; } @@ -443,14 +457,14 @@ export default class JitsiTrack extends EventEmitter { * a local track if the audio level was measured outside of the * peerconnection (see /modules/statistics/LocalStatsCollector.js). */ - setAudioLevel(audioLevel, tpc) { + setAudioLevel(audioLevel: number, tpc?: TraceablePeerConnection): void { let newAudioLevel = audioLevel; // When using getSynchornizationSources on the audio receiver to gather audio levels for // remote tracks, browser reports last known audio levels even when the remote user is // audio muted, we need to reset the value to zero here so that the audio levels are cleared. // Remote tracks have the tpc info present while local tracks do not. - if (browser.supportsReceiverStats() && typeof tpc !== 'undefined' && this.isMuted()) { + if (browser.supportsReceiverStats() && typeof tpc !== 'undefined' && this.isWebRTCTrackMuted()) { newAudioLevel = 0; } @@ -481,23 +495,21 @@ export default class JitsiTrack extends EventEmitter { * @emits JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED * @returns {Promise} */ - setAudioOutput(audioOutputDeviceId) { + setAudioOutput(audioOutputDeviceId: string): Promise { if (!RTCUtils.isDeviceChangeAvailable('output')) { return Promise.reject( new Error('Audio output device change is not supported')); } - // All audio communication is done through audio tracks, so ignore - // changing audio output for video tracks at all. if (this.isVideoTrack()) { return Promise.resolve(); } return ( Promise.all( - this.containers.map( - element => - element.setSinkId(audioOutputDeviceId) + this.containers.map(element => { + if (element instanceof HTMLMediaElement) { + return element.setSinkId(audioOutputDeviceId) .catch(error => { logger.warn( 'Failed to change audio output device on' @@ -506,13 +518,18 @@ export default class JitsiTrack extends EventEmitter { element, error); throw error; - })) + }); + } else { + return Promise.resolve(); + } + }) ) - .then(() => { - this.emit( - JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED, - audioOutputDeviceId); - })); + .then(() => { + this.emit( + JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED, + audioOutputDeviceId); + }) + ); } /** @@ -520,7 +537,7 @@ export default class JitsiTrack extends EventEmitter { * @param {String} name - The name to be assigned to the track. * @returns {void} */ - setSourceName(name) { // eslint-disable-line no-unused-vars + setSourceName(name: string): void { // eslint-disable-line no-unused-vars // Should be defined by the classes that are extending JitsiTrack } } diff --git a/modules/browser/BrowserCapabilities.js b/modules/browser/BrowserCapabilities.js index 71a751c72d..fdf0733357 100644 --- a/modules/browser/BrowserCapabilities.js +++ b/modules/browser/BrowserCapabilities.js @@ -27,6 +27,15 @@ export default class BrowserCapabilities extends BrowserDetection { * * @return {boolean} true if the current browser supports this strategy or false otherwise. */ + + isFirefox() { + return this.getBrowser() === 'firefox'; // Assuming getBrowser() returns 'firefox' for Firefox + } + + isWebKitBased() { + return this.getBrowser() === 'WebKit'; + } + doesVideoMuteByStreamRemove() { return this.isChromiumBased() || this.isWebKitBased() || this.isFirefox(); } diff --git a/package-lock.json b/package-lock.json index 8fc66cb534..12ccf22871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "Apache-2.0", "dependencies": { "@jitsi/js-utils": "2.2.1", - "@jitsi/logger": "2.0.2", + "@jitsi/logger": "^2.0.2", "@jitsi/precall-test": "1.0.6", "@jitsi/rtcstats": "9.7.0", "@testrtc/watchrtc-sdk": "1.38.2", diff --git a/package.json b/package.json index 81ec7e3552..86a013a1fe 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "readmeFilename": "README.md", "dependencies": { "@jitsi/js-utils": "2.2.1", - "@jitsi/logger": "2.0.2", + "@jitsi/logger": "^2.0.2", "@jitsi/precall-test": "1.0.6", "@jitsi/rtcstats": "9.7.0", "@testrtc/watchrtc-sdk": "1.38.2",