diff --git a/packages/hms-video-store/src/sdk/index.ts b/packages/hms-video-store/src/sdk/index.ts index 4bfda90f5e..e0a015a817 100644 --- a/packages/hms-video-store/src/sdk/index.ts +++ b/packages/hms-video-store/src/sdk/index.ts @@ -24,6 +24,7 @@ import { HMSAction } from '../error/HMSAction'; import { HMSException } from '../error/HMSException'; import { EventBus } from '../events/EventBus'; import { + HMSAudioCodec, HMSChangeMultiTrackStateParams, HMSConfig, HMSConnectionQualityListener, @@ -37,6 +38,7 @@ import { HMSRole, HMSRoleChangeRequest, HMSScreenShareConfig, + HMSVideoCodec, TokenRequest, TokenRequestOptions, } from '../interfaces'; @@ -51,6 +53,7 @@ import { RTMPRecordingConfig } from '../interfaces/rtmp-recording-config'; import InitialSettings from '../interfaces/settings'; import { HMSAudioListener, HMSPeerUpdate, HMSTrackUpdate, HMSUpdateListener } from '../interfaces/update-listener'; import { PlaylistManager, TranscriptionConfig } from '../internal'; +import { HMSAudioTrackSettingsBuilder, HMSVideoTrackSettingsBuilder } from '../media/settings'; import { HMSLocalStream } from '../media/streams/HMSLocalStream'; import { HMSLocalAudioTrack, @@ -95,7 +98,7 @@ import HMSLogger, { HMSLogLevel } from '../utils/logger'; import { HMSAudioContextHandler } from '../utils/media'; import { isNode } from '../utils/support'; import { workerSleep } from '../utils/timer-utils'; -import { validateMediaDevicesExistence, validateRTCPeerConnection } from '../utils/validations'; +import { validateMediaDevicesExistence, validatePublishParams, validateRTCPeerConnection } from '../utils/validations'; const INITIAL_STATE = { published: false, @@ -954,7 +957,8 @@ export class HMSSdk implements HMSInterface { const TrackKlass = type === 'audio' ? HMSLocalAudioTrack : HMSLocalVideoTrack; const hmsTrack = new TrackKlass(stream, track, source, this.eventBus); - this.setPlaylistSettings({ + await this.applySettings(hmsTrack); + await this.setPlaylistSettings({ track, hmsTrack, source, @@ -1603,4 +1607,42 @@ export class HMSSdk implements HMSInterface { this.playlistManager.stop(HMSPlaylistType.video); } } + + // eslint-disable-next-line complexity + private async applySettings(track: HMSLocalTrack) { + validatePublishParams(this.store); + const publishParams = this.store.getPublishParams(); + // this is not needed but added for avoiding ? later + if (!publishParams) { + return; + } + if (track instanceof HMSLocalVideoTrack) { + const publishKey = track.source === 'regular' ? 'video' : track.source === 'screen' ? 'screen' : ''; + if (!publishKey || !publishParams.allowed.includes(publishKey)) { + return; + } + const video = publishParams[publishKey]; + if (!video) { + return; + } + const settings = new HMSVideoTrackSettingsBuilder() + .codec(video.codec as HMSVideoCodec) + .maxBitrate(video.bitRate) + .maxFramerate(video.frameRate) + .setWidth(video.width) + .setHeight(video.height) + .build(); + + await track.setSettings(settings); + } else if (track instanceof HMSLocalAudioTrack) { + if (!publishParams.allowed.includes('audio')) { + return; + } + const settings = new HMSAudioTrackSettingsBuilder() + .codec(publishParams.audio.codec as HMSAudioCodec) + .maxBitrate(publishParams.audio.bitRate) + .build(); + await track.setSettings(settings); + } + } } diff --git a/packages/hms-video-store/src/transport/RetryScheduler.ts b/packages/hms-video-store/src/transport/RetryScheduler.ts index 8695e5a577..0feaf8c006 100644 --- a/packages/hms-video-store/src/transport/RetryScheduler.ts +++ b/packages/hms-video-store/src/transport/RetryScheduler.ts @@ -1,7 +1,7 @@ import { Dependencies as TFCDependencies, TransportFailureCategory as TFC } from './models/TransportFailureCategory'; import { TransportState } from './models/TransportState'; import { HMSException } from '../error/HMSException'; -import { MAX_TRANSPORT_RETRIES, MAX_TRANSPORT_RETRY_DELAY } from '../utils/constants'; +import { MAX_TRANSPORT_RETRY_TIME } from '../utils/constants'; import HMSLogger from '../utils/logger'; import { PromiseWithCallbacks } from '../utils/promise'; @@ -23,7 +23,7 @@ interface ScheduleTaskParams { error: HMSException; task: RetryTask; originalState: TransportState; - maxFailedRetries?: number; + maxRetryTime?: number; changeState?: boolean; } @@ -42,10 +42,10 @@ export class RetryScheduler { error, task, originalState, - maxFailedRetries = MAX_TRANSPORT_RETRIES, + maxRetryTime = MAX_TRANSPORT_RETRY_TIME, changeState = true, }: ScheduleTaskParams) { - await this.scheduleTask({ category, error, changeState, task, originalState, maxFailedRetries }); + await this.scheduleTask({ category, error, changeState, task, originalState, maxRetryTime, failedAt: Date.now() }); } reset() { @@ -65,9 +65,10 @@ export class RetryScheduler { changeState, task, originalState, - maxFailedRetries = MAX_TRANSPORT_RETRIES, + failedAt, + maxRetryTime = MAX_TRANSPORT_RETRY_TIME, failedRetryCount = 0, - }: ScheduleTaskParams & { failedRetryCount?: number }): Promise { + }: ScheduleTaskParams & { failedAt: number; failedRetryCount?: number }): Promise { HMSLogger.d(this.TAG, 'schedule: ', { category: TFC[category], error }); // First schedule call @@ -113,8 +114,9 @@ export class RetryScheduler { } } - if (failedRetryCount >= maxFailedRetries || hasFailedDependency) { - error.description += `. [${TFC[category]}] Could not recover after ${failedRetryCount} tries`; + const timeElapsedSinceError = Date.now() - failedAt; + if (timeElapsedSinceError >= maxRetryTime || hasFailedDependency) { + error.description += `. [${TFC[category]}] Could not recover after ${timeElapsedSinceError} milliseconds`; if (hasFailedDependency) { error.description += ` Could not recover all of it's required dependencies - [${(dependencies as Array) @@ -144,7 +146,7 @@ export class RetryScheduler { this.onStateChange(TransportState.Reconnecting, error); } - const delay = this.getDelayForRetryCount(category, failedRetryCount); + const delay = this.getDelayForRetryCount(category); HMSLogger.d( this.TAG, @@ -171,7 +173,10 @@ export class RetryScheduler { if (changeState && this.inProgress.size === 0) { this.onStateChange(originalState); } - HMSLogger.d(this.TAG, `schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Recovered ♻️`); + HMSLogger.d( + this.TAG, + `schedule: [${TFC[category]}] [failedRetryCount=${failedRetryCount}] Recovered ♻️ after ${timeElapsedSinceError}ms`, + ); } else { await this.scheduleTask({ category, @@ -179,25 +184,23 @@ export class RetryScheduler { changeState, task, originalState, - maxFailedRetries, + maxRetryTime, + failedAt, failedRetryCount: failedRetryCount + 1, }); } } - private getBaseDelayForTask(category: TFC, n: number) { + private getDelayForRetryCount(category: TFC) { + const jitter = category === TFC.JoinWSMessageFailed ? Math.random() * 2 : Math.random(); + let delaySeconds = 0; if (category === TFC.JoinWSMessageFailed) { // linear backoff(2 + jitter for every retry) - return 2; + delaySeconds = 2 + jitter; + } else if (category === TFC.SignalDisconnect) { + delaySeconds = 1; } - // 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); + return delaySeconds * 1000; } private async setTimeoutPromise(task: () => Promise, delay: number): Promise { diff --git a/packages/hms-video-store/src/transport/index.ts b/packages/hms-video-store/src/transport/index.ts index e244dc88b6..11b418a416 100644 --- a/packages/hms-video-store/src/transport/index.ts +++ b/packages/hms-video-store/src/transport/index.ts @@ -36,7 +36,6 @@ import { ISignalEventsObserver } from '../signal/ISignalEventsObserver'; import JsonRpcSignal from '../signal/jsonrpc'; import { ICE_DISCONNECTION_TIMEOUT, - MAX_TRANSPORT_RETRIES, PROTOCOL_SPEC, PROTOCOL_VERSION, PUBLISH_STATS_PUSH_INTERVAL, @@ -352,7 +351,6 @@ export default class HMSTransport { error, task, originalState: this.state, - maxFailedRetries: MAX_TRANSPORT_RETRIES, changeState: false, }); } else { @@ -923,7 +921,6 @@ export default class HMSTransport { error: hmsError, task, originalState: TransportState.Joined, - maxFailedRetries: 3, changeState: false, }); } else { @@ -1087,7 +1084,6 @@ export default class HMSTransport { error, task: this.retrySubscribeIceFailedTask, originalState: TransportState.Joined, - maxFailedRetries: 1, }); } } diff --git a/packages/hms-video-store/src/utils/constants.ts b/packages/hms-video-store/src/utils/constants.ts index 4e9632bc96..5f237a85d3 100644 --- a/packages/hms-video-store/src/utils/constants.ts +++ b/packages/hms-video-store/src/utils/constants.ts @@ -3,13 +3,12 @@ export const API_DATA_CHANNEL = 'ion-sfu'; export const ANALYTICS_BUFFER_SIZE = 100; /** - * Maximum number of retries that transport-layer will try + * Maximum time that transport-layer will try * before giving up on the connection and returning a failure * * Refer https://100ms.atlassian.net/browse/HMS-2369 */ -export const MAX_TRANSPORT_RETRIES = 5; -export const MAX_TRANSPORT_RETRY_DELAY = 60; +export const MAX_TRANSPORT_RETRY_TIME = 60_000; export const DEFAULT_SIGNAL_PING_TIMEOUT = 12_000; export const DEFAULT_SIGNAL_PING_INTERVAL = 3_000; diff --git a/packages/hms-video-store/src/utils/validations.ts b/packages/hms-video-store/src/utils/validations.ts index fc75902e44..a1a1824c16 100644 --- a/packages/hms-video-store/src/utils/validations.ts +++ b/packages/hms-video-store/src/utils/validations.ts @@ -1,5 +1,7 @@ import HMSLogger from './logger'; import { ErrorFactory } from '../error/ErrorFactory'; +import { HMSAction } from '../error/HMSAction'; +import { Store } from '../sdk/store'; const TAG = `[VALIDATIONS]`; @@ -32,3 +34,13 @@ export const validateMediaDevicesExistence = () => { throw error; } }; + +export const validatePublishParams = (store: Store) => { + const publishParams = store.getPublishParams(); + if (!publishParams) { + throw ErrorFactory.GenericErrors.NotConnected( + HMSAction.VALIDATION, + 'call addTrack after preview or join is successful', + ); + } +};