@100mslive/hms-video-store
Advanced tools
Comparing version 0.12.20-alpha.3 to 0.12.20-alpha.4
@@ -34,3 +34,2 @@ import { ServerError } from './internal'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -37,0 +36,0 @@ effectsKey?: string; |
@@ -12,4 +12,3 @@ import { HMSVideoTrack } from './HMSVideoTrack'; | ||
private bizTrackId; | ||
private disableNoneLayerRequest; | ||
constructor(stream: HMSRemoteStream, track: MediaStreamTrack, source?: string, disableNoneLayerRequest?: boolean); | ||
constructor(stream: HMSRemoteStream, track: MediaStreamTrack, source?: string); | ||
setTrackId(trackId: string): void; | ||
@@ -16,0 +15,0 @@ get trackId(): string; |
@@ -40,3 +40,2 @@ import { HMSPeerID } from './peer'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -43,0 +42,0 @@ effectsKey?: string; |
@@ -18,3 +18,2 @@ import { HMSHLS, HMSRecording, HMSRoom, HMSRTMP, HMSTranscriptionInfo } from '../../interfaces/room'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -21,0 +20,0 @@ effectsKey?: string; |
@@ -58,4 +58,3 @@ /** | ||
FLAG_NOISE_CANCELLATION = "noiseCancellation", | ||
FLAG_SCALE_SCREENSHARE_BASED_ON_PIXELS = "scaleScreenshareBasedOnPixels", | ||
FLAG_DISABLE_NONE_LAYER_REQUEST = "disableNoneLayerRequest" | ||
FLAG_SCALE_SCREENSHARE_BASED_ON_PIXELS = "scaleScreenshareBasedOnPixels" | ||
} |
@@ -21,3 +21,3 @@ import { TransportFailureCategory as TFC } from './models/TransportFailureCategory'; | ||
originalState: TransportState; | ||
maxRetryTime?: number; | ||
maxFailedRetries?: number; | ||
changeState?: boolean; | ||
@@ -32,6 +32,7 @@ } | ||
constructor(onStateChange: (state: TransportState, error?: HMSException) => Promise<void>, sendEvent: (error: HMSException, category: TFC) => void); | ||
schedule({ category, error, task, originalState, maxRetryTime, changeState, }: ScheduleTaskParams): Promise<void>; | ||
schedule({ category, error, task, originalState, maxFailedRetries, changeState, }: ScheduleTaskParams): Promise<void>; | ||
reset(): void; | ||
isTaskInProgress(category: TFC): boolean; | ||
private scheduleTask; | ||
private getBaseDelayForTask; | ||
private getDelayForRetryCount; | ||
@@ -38,0 +39,0 @@ private setTimeoutPromise; |
@@ -5,3 +5,3 @@ export declare const RENEGOTIATION_CALLBACK_ID = "renegotiation-callback-id"; | ||
/** | ||
* Maximum time that transport-layer will try | ||
* Maximum number of retries that transport-layer will try | ||
* before giving up on the connection and returning a failure | ||
@@ -11,3 +11,4 @@ * | ||
*/ | ||
export declare const MAX_TRANSPORT_RETRY_TIME = 60000; | ||
export declare const MAX_TRANSPORT_RETRIES = 5; | ||
export declare const MAX_TRANSPORT_RETRY_DELAY = 60; | ||
export declare const DEFAULT_SIGNAL_PING_TIMEOUT = 12000; | ||
@@ -14,0 +15,0 @@ export declare const DEFAULT_SIGNAL_PING_INTERVAL = 3000; |
{ | ||
"version": "0.12.20-alpha.3", | ||
"version": "0.12.20-alpha.4", | ||
"license": "MIT", | ||
@@ -76,3 +76,3 @@ "repository": { | ||
], | ||
"gitHead": "7432edcc726ca36bf1ffeab7e0ab8ab16f461ac3" | ||
"gitHead": "54eb16e9c238cb6f2e1f4fe99495f5cb26b83330" | ||
} |
@@ -101,7 +101,4 @@ import EventEmitter from 'eventemitter2'; | ||
const remote = this.remoteStreams.get(streamId)!; | ||
const isAudioTrack = e.track.kind === 'audio'; | ||
const TrackCls = isAudioTrack ? HMSRemoteAudioTrack : HMSRemoteVideoTrack; | ||
const track = isAudioTrack | ||
? new TrackCls(remote, e.track) | ||
: new TrackCls(remote, e.track, undefined, this.isFlagEnabled(InitFlags.FLAG_DISABLE_NONE_LAYER_REQUEST)); | ||
const TrackCls = e.track.kind === 'audio' ? HMSRemoteAudioTrack : HMSRemoteVideoTrack; | ||
const track = new TrackCls(remote, e.track); | ||
// reset the simulcast layer to none when new video tracks are added, UI will subscribe when required | ||
@@ -108,0 +105,0 @@ if (e.track.kind === 'video') { |
@@ -262,3 +262,3 @@ import { DeviceStorageManager } from './DeviceStorage'; | ||
// false positives when device is removed, because the other available device | ||
// gets the deviceId as default once this device is removed | ||
// get's the deviceId as default once this device is removed | ||
const nextDevice = this.audioInput.find(device => { | ||
@@ -265,0 +265,0 @@ return device.deviceId !== 'default' && defaultDevice.label.includes(device.label); |
@@ -37,3 +37,2 @@ import { ServerError } from './internal'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -40,0 +39,0 @@ effectsKey?: string; |
@@ -187,4 +187,4 @@ import isEqual from 'lodash.isequal'; | ||
// Replace silent empty track or muted track(happens when microphone is disabled from address bar in iOS) with an actual audio track, if enabled. | ||
if (value && (isEmptyTrack(this.nativeTrack) || this.nativeTrack.muted)) { | ||
// Replace silent empty track with an actual audio track, if enabled. | ||
if (value && isEmptyTrack(this.nativeTrack)) { | ||
await this.replaceTrackWith(this.settings); | ||
@@ -191,0 +191,0 @@ } |
@@ -569,3 +569,8 @@ import isEqual from 'lodash.isequal'; | ||
if (this.enabled) { | ||
await this.setEnabled(false); | ||
const track = await this.replaceTrackWithBlank(); | ||
await this.replaceSender(track, this.enabled); | ||
this.nativeTrack?.stop(); | ||
this.nativeTrack = track; | ||
} else { | ||
await this.replaceSender(this.nativeTrack, false); | ||
} | ||
@@ -580,5 +585,8 @@ // started interruption event | ||
} else { | ||
HMSLogger.d(this.TAG, 'visibility visible, restoring track state', this.enabledStateBeforeBackground); | ||
HMSLogger.d(this.TAG, 'visibility visibile, restoring track state', this.enabledStateBeforeBackground); | ||
if (this.enabledStateBeforeBackground) { | ||
await this.setEnabled(true); | ||
} else { | ||
this.nativeTrack.enabled = this.enabledStateBeforeBackground; | ||
await this.replaceSender(this.nativeTrack, this.enabledStateBeforeBackground); | ||
} | ||
@@ -593,3 +601,4 @@ // ended interruption event | ||
} | ||
this.eventBus.localVideoEnabled.publish({ enabled: this.nativeTrack.enabled, track: this }); | ||
}; | ||
} |
@@ -21,7 +21,5 @@ import { HMSVideoTrack } from './HMSVideoTrack'; | ||
private bizTrackId!: string; | ||
private disableNoneLayerRequest = false; | ||
constructor(stream: HMSRemoteStream, track: MediaStreamTrack, source?: string, disableNoneLayerRequest?: boolean) { | ||
constructor(stream: HMSRemoteStream, track: MediaStreamTrack, source?: string) { | ||
super(stream, track, source); | ||
this.disableNoneLayerRequest = !!disableNoneLayerRequest; | ||
this.setVideoHandler(new VideoElementManager(this)); | ||
@@ -176,6 +174,3 @@ } | ||
private async updateLayer(source: string) { | ||
const newLayer = | ||
(this.degraded || !this.enabled || !this.hasSinks()) && !this.disableNoneLayerRequest | ||
? HMSSimulcastLayer.NONE | ||
: this.preferredLayer; | ||
const newLayer = this.degraded || !this.enabled || !this.hasSinks() ? HMSSimulcastLayer.NONE : this.preferredLayer; | ||
if (!this.shouldSendVideoLayer(newLayer, source)) { | ||
@@ -182,0 +177,0 @@ return; |
@@ -22,3 +22,3 @@ import { HMSRemoteVideoTrack } from './HMSRemoteVideoTrack'; | ||
nativeTrack = { id: trackId, kind: 'video', enabled: true } as MediaStreamTrack; | ||
track = new HMSRemoteVideoTrack(stream, nativeTrack, 'regular', false); | ||
track = new HMSRemoteVideoTrack(stream, nativeTrack, 'regular'); | ||
window.MediaStream = jest.fn().mockImplementation(() => ({ | ||
@@ -160,61 +160,1 @@ addTrack: jest.fn(), | ||
}); | ||
describe('HMSRemoteVideoTrack with disableNoneLayerRequest', () => { | ||
let stream: HMSRemoteStream; | ||
let sendOverApiDataChannelWithResponse: jest.Mock; | ||
let track: HMSRemoteVideoTrack; | ||
let nativeTrack: MediaStreamTrack; | ||
let videoElement: HTMLVideoElement; | ||
const trackId = 'test-track-id'; | ||
beforeEach(() => { | ||
videoElement = document.createElement('video'); | ||
sendOverApiDataChannelWithResponse = jest.fn(); | ||
const connection = { sendOverApiDataChannelWithResponse } as unknown as HMSSubscribeConnection; | ||
const nativeStream = new MediaStream(); | ||
stream = new HMSRemoteStream(nativeStream, connection); | ||
nativeTrack = { id: trackId, kind: 'video', enabled: true } as MediaStreamTrack; | ||
track = new HMSRemoteVideoTrack(stream, nativeTrack, 'regular', true); // disableNoneLayerRequest flag is set | ||
track.setTrackId(trackId); | ||
window.MediaStream = jest.fn().mockImplementation(() => ({ | ||
addTrack: jest.fn(), | ||
})); | ||
}); | ||
const expectLayersSent = (layers: HMSSimulcastLayer[]) => { | ||
const allCalls = sendOverApiDataChannelWithResponse.mock.calls; | ||
expect(allCalls.length).toBe(layers.length); | ||
for (let i = 0; i < allCalls.length; i++) { | ||
const data = allCalls[i][0]; | ||
expect(data.params.max_spatial_layer).toBe(layers[i]); | ||
} | ||
}; | ||
const sfuDegrades = () => { | ||
track.setLayerFromServer({ | ||
subscriber_degraded: true, | ||
expected_layer: HMSSimulcastLayer.HIGH, | ||
current_layer: HMSSimulcastLayer.NONE, | ||
publisher_degraded: false, | ||
track_id: trackId, | ||
}); | ||
}; | ||
test('disableNoneLayerRequest - degradation', async () => { | ||
await track.addSink(videoElement); | ||
expectLayersSent([HMSSimulcastLayer.HIGH]); | ||
sfuDegrades(); | ||
expectLayersSent([HMSSimulcastLayer.HIGH]); | ||
}); | ||
test('disableNoneLayerRequest - mute and removeSink', async () => { | ||
await track.addSink(videoElement); | ||
track.setEnabled(false); | ||
expectLayersSent([HMSSimulcastLayer.HIGH]); | ||
await track.removeSink(videoElement); | ||
expectLayersSent([HMSSimulcastLayer.HIGH]); | ||
}); | ||
}); |
@@ -70,8 +70,3 @@ import { TrackManager } from './TrackManager'; | ||
emptyTrack.enabled = !trackInfo.mute; | ||
const track = new HMSRemoteVideoTrack( | ||
remoteStream, | ||
emptyTrack, | ||
trackInfo.source, | ||
this.store.getRoom()?.disableNoneLayerRequest, | ||
); | ||
const track = new HMSRemoteVideoTrack(remoteStream, emptyTrack, trackInfo.source); | ||
track.setTrackId(trackInfo.track_id); | ||
@@ -78,0 +73,0 @@ track.peerId = hmsPeer.peerId; |
@@ -162,3 +162,2 @@ import { areArraysEqual } from './sdkUtils/storeMergeUtils'; | ||
isEffectsEnabled: sdkRoom.isEffectsEnabled, | ||
disableNoneLayerRequest: sdkRoom.disableNoneLayerRequest, | ||
isVBEnabled: sdkRoom.isVBEnabled, | ||
@@ -165,0 +164,0 @@ effectsKey: sdkRoom.effectsKey, |
@@ -43,3 +43,2 @@ import { HMSPeerID } from './peer'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -46,0 +45,0 @@ effectsKey?: string; |
@@ -19,3 +19,2 @@ import { HMSHLS, HMSRecording, HMSRoom, HMSRTMP, HMSTranscriptionInfo } from '../../interfaces/room'; | ||
isEffectsEnabled?: boolean; | ||
disableNoneLayerRequest?: boolean; | ||
isVBEnabled?: boolean; | ||
@@ -22,0 +21,0 @@ effectsKey?: string; |
@@ -65,3 +65,2 @@ /** | ||
FLAG_SCALE_SCREENSHARE_BASED_ON_PIXELS = 'scaleScreenshareBasedOnPixels', | ||
FLAG_DISABLE_NONE_LAYER_REQUEST = 'disableNoneLayerRequest', | ||
} |
@@ -39,2 +39,3 @@ import { JoinParameters } from './models/JoinParameters'; | ||
ICE_DISCONNECTION_TIMEOUT, | ||
MAX_TRANSPORT_RETRIES, | ||
PROTOCOL_SPEC, | ||
@@ -355,2 +356,3 @@ PROTOCOL_VERSION, | ||
originalState: this.state, | ||
maxFailedRetries: MAX_TRANSPORT_RETRIES, | ||
changeState: false, | ||
@@ -926,2 +928,3 @@ }); | ||
originalState: TransportState.Joined, | ||
maxFailedRetries: 3, | ||
changeState: false, | ||
@@ -1090,2 +1093,3 @@ }); | ||
originalState: TransportState.Joined, | ||
maxFailedRetries: 1, | ||
}); | ||
@@ -1112,3 +1116,2 @@ } | ||
room.isEffectsEnabled = this.isFlagEnabled(InitFlags.FLAG_EFFECTS_SDK_ENABLED); | ||
room.disableNoneLayerRequest = this.isFlagEnabled(InitFlags.FLAG_DISABLE_NONE_LAYER_REQUEST); | ||
room.isVBEnabled = this.isFlagEnabled(InitFlags.FLAG_VB_ENABLED); | ||
@@ -1115,0 +1118,0 @@ room.isHipaaEnabled = this.isFlagEnabled(InitFlags.FLAG_HIPAA_ENABLED); |
import { Dependencies as TFCDependencies, TransportFailureCategory as TFC } from './models/TransportFailureCategory'; | ||
import { TransportState } from './models/TransportState'; | ||
import { HMSException } from '../error/HMSException'; | ||
import { MAX_TRANSPORT_RETRY_TIME } from '../utils/constants'; | ||
import { MAX_TRANSPORT_RETRIES, MAX_TRANSPORT_RETRY_DELAY } from '../utils/constants'; | ||
import HMSLogger from '../utils/logger'; | ||
@@ -26,3 +26,3 @@ import { PromiseWithCallbacks } from '../utils/promise'; | ||
originalState: TransportState; | ||
maxRetryTime?: number; | ||
maxFailedRetries?: number; | ||
changeState?: boolean; | ||
@@ -46,6 +46,6 @@ } | ||
originalState, | ||
maxRetryTime = MAX_TRANSPORT_RETRY_TIME, | ||
maxFailedRetries = MAX_TRANSPORT_RETRIES, | ||
changeState = true, | ||
}: ScheduleTaskParams) { | ||
await this.scheduleTask({ category, error, changeState, task, originalState, maxRetryTime, failedAt: Date.now() }); | ||
await this.scheduleTask({ category, error, changeState, task, originalState, maxFailedRetries }); | ||
} | ||
@@ -70,6 +70,5 @@ | ||
originalState, | ||
failedAt, | ||
maxRetryTime = MAX_TRANSPORT_RETRY_TIME, | ||
maxFailedRetries = MAX_TRANSPORT_RETRIES, | ||
failedRetryCount = 0, | ||
}: ScheduleTaskParams & { failedAt: number; failedRetryCount?: number }): Promise<void> { | ||
}: ScheduleTaskParams & { failedRetryCount?: number }): Promise<void> { | ||
HMSLogger.d(this.TAG, 'schedule: ', { category: TFC[category], error }); | ||
@@ -120,5 +119,4 @@ | ||
const timeElapsedSinceError = Date.now() - failedAt; | ||
if (timeElapsedSinceError >= maxRetryTime || hasFailedDependency) { | ||
error.description += `. [${TFC[category]}] Could not recover after ${timeElapsedSinceError} milliseconds`; | ||
if (failedRetryCount >= maxFailedRetries || hasFailedDependency) { | ||
error.description += `. [${TFC[category]}] Could not recover after ${failedRetryCount} tries`; | ||
@@ -153,3 +151,3 @@ if (hasFailedDependency) { | ||
const delay = this.getDelayForRetryCount(category); | ||
const delay = this.getDelayForRetryCount(category, failedRetryCount); | ||
@@ -181,6 +179,3 @@ HMSLogger.d( | ||
} | ||
HMSLogger.d( | ||
this.TAG, | ||
`schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Recovered ♻️ after ${timeElapsedSinceError}ms`, | ||
); | ||
HMSLogger.d(this.TAG, `schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Recovered ♻️`); | ||
} else { | ||
@@ -193,4 +188,3 @@ await this.scheduleTask({ | ||
originalState, | ||
maxRetryTime, | ||
failedAt, | ||
maxFailedRetries, | ||
failedRetryCount: failedRetryCount + 1, | ||
@@ -201,14 +195,17 @@ }); | ||
private getDelayForRetryCount(category: TFC) { | ||
const jitter = category === TFC.JoinWSMessageFailed ? Math.random() * 2 : Math.random(); | ||
let delaySeconds = 0; | ||
private getBaseDelayForTask(category: TFC, n: number) { | ||
if (category === TFC.JoinWSMessageFailed) { | ||
// linear backoff(2 + jitter for every retry) | ||
delaySeconds = 2 + jitter; | ||
} else if (category === TFC.SignalDisconnect) { | ||
delaySeconds = 1; | ||
return 2; | ||
} | ||
return delaySeconds * 1000; | ||
// exponential backoff | ||
return Math.pow(2, n); | ||
} | ||
private getDelayForRetryCount(category: TFC, n: number) { | ||
const delay = this.getBaseDelayForTask(category, n); | ||
const jitter = category === TFC.JoinWSMessageFailed ? Math.random() * 2 : Math.random(); | ||
return Math.round(Math.min(delay + jitter, MAX_TRANSPORT_RETRY_DELAY) * 1000); | ||
} | ||
private async setTimeoutPromise<T>(task: () => Promise<T>, delay: number): Promise<T> { | ||
@@ -215,0 +212,0 @@ return new Promise((resolve, reject) => { |
@@ -6,3 +6,3 @@ export const RENEGOTIATION_CALLBACK_ID = 'renegotiation-callback-id'; | ||
/** | ||
* Maximum time that transport-layer will try | ||
* Maximum number of retries that transport-layer will try | ||
* before giving up on the connection and returning a failure | ||
@@ -12,3 +12,4 @@ * | ||
*/ | ||
export const MAX_TRANSPORT_RETRY_TIME = 60_000; | ||
export const MAX_TRANSPORT_RETRIES = 5; | ||
export const MAX_TRANSPORT_RETRY_DELAY = 60; | ||
@@ -15,0 +16,0 @@ export const DEFAULT_SIGNAL_PING_TIMEOUT = 12_000; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4797249
40746