diff --git a/package-lock.json b/package-lock.json index fb6b3962..46cd63b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,7 @@ "swc-loader": "0.1.15", "ts-jest": "^29.1.0", "ts-node": "10.9.1", - "typescript": "5.5.4", + "typescript": "^5.6.3", "underscore": "^1.13.1", "vite": "^5.2.10", "vite-plugin-dts": "~3.8.1", @@ -19719,16 +19719,6 @@ "node": ">= 8" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -39493,13 +39483,6 @@ "node": ">= 14" } }, - "node_modules/package-manager-detector": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", - "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==", - "dev": true, - "license": "MIT" - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -43196,9 +43179,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index aef74d4b..61356642 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "swc-loader": "0.1.15", "ts-jest": "^29.1.0", "ts-node": "10.9.1", - "typescript": "5.5.4", + "typescript": "^5.6.3", "underscore": "^1.13.1", "vite": "^5.2.10", "vite-plugin-dts": "~3.8.1", diff --git a/packages/millicast-sdk/src/Director.ts b/packages/millicast-sdk/src/Director.ts index 8de7bed2..b7b8ad5a 100644 --- a/packages/millicast-sdk/src/Director.ts +++ b/packages/millicast-sdk/src/Director.ts @@ -234,7 +234,11 @@ const parseIncomingDirectorResponse = (directorResponse: { data: DirectorRespons if (Director.getLiveDomain()) { const domainRegex = /\/\/(.*?)\// const urlsParsed = directorResponse.data.urls.map((url) => { - const matched = domainRegex.exec(url) as RegExpExecArray + const matched = domainRegex.exec(url) + if (!matched) { + logger.warn('Unable to parse incoming director response') + return url + } return url.replace(matched[1], Director.getLiveDomain()) }) directorResponse.data.urls = urlsParsed diff --git a/packages/millicast-sdk/src/Logger.ts b/packages/millicast-sdk/src/Logger.ts index a46b7178..b92f9571 100644 --- a/packages/millicast-sdk/src/Logger.ts +++ b/packages/millicast-sdk/src/Logger.ts @@ -293,12 +293,14 @@ const Logger = { * * // Output: Diagnostics object with specified configuration */ - diagnose: (config: DiagnosticsOptions = { - statsCount: 60, - historySize: 1000, - minLogLevel: 'TRACE', - statsFormat: 'JSON', - }): DiagnosticsObject | CMCDDiagnostics => { + diagnose: ( + config: DiagnosticsOptions = { + statsCount: 60, + historySize: 1000, + minLogLevel: 'TRACE', + statsFormat: 'JSON', + } + ): DiagnosticsObject | CMCDDiagnostics => { let finalConfig const defaultConfig = { statsCount: 60, @@ -315,7 +317,7 @@ const Logger = { } const { statsCount, historySize, minLogLevel, statsFormat } = finalConfig const result = Diagnostics.get(statsCount, statsFormat) - const history = Logger.getHistory() as string[] + const history = Logger.getHistory() if (!Number.isInteger(historySize) || historySize <= 0) { throw new Error('Invalid Argument Exception : historySize must be a positive integer.') diff --git a/packages/millicast-sdk/src/PeerConnection.ts b/packages/millicast-sdk/src/PeerConnection.ts index 1557bd50..7a3552a9 100644 --- a/packages/millicast-sdk/src/PeerConnection.ts +++ b/packages/millicast-sdk/src/PeerConnection.ts @@ -6,6 +6,8 @@ import UserAgent from './utils/UserAgent' import Logger from './Logger' import { SdpOptions, MillicastCapability, ICodecs, PeerConnectionConfig } from './types/PeerConnection.types' import { AudioCodec, VideoCodec } from './types/Codecs.types' +import { isAudioCodec, isVideoCodec } from './utils/Codecs' +import { typedKeys } from './utils/ObjectUtils' const logger = Logger.get('PeerConnection') @@ -39,17 +41,17 @@ const localSDPOptions = { */ export default class PeerConnection extends EventEmitter { public mode: 'Publisher' | 'Viewer' | null - public sessionDescription: RTCSessionDescriptionInit | null public peer: RTCPeerConnection | null public peerConnectionStats: PeerConnectionStats | null public transceiverMap: Map< RTCRtpTransceiver, (value: RTCRtpTransceiver | PromiseLike) => void > + public sessionDescription?: RTCSessionDescriptionInit + constructor() { super() this.mode = null - this.sessionDescription = null this.peer = null this.peerConnectionStats = null this.transceiverMap = new Map() @@ -293,7 +295,7 @@ export default class PeerConnection extends EventEmitter { const browserCapabilities = RTCRtpSender.getCapabilities(kind) as MillicastCapability if (browserCapabilities) { - const codecs: { [key: string]: ICodecs } = {} + const codecs: { [key in VideoCodec | AudioCodec]?: ICodecs } = {} let regex = new RegExp(`^video/(${Object.values(VideoCodec).join('|')})x?$`, 'i') if (kind === 'audio') { @@ -305,23 +307,25 @@ export default class PeerConnection extends EventEmitter { } for (const codec of browserCapabilities.codecs) { - const matches = codec.mimeType.match(regex) + const matches = codec.mimeType?.match(regex) if (matches) { const codecName = matches[1].toLowerCase() - codecs[codecName] = { ...codecs[codecName], mimeType: codec.mimeType } - if (codec.scalabilityModes) { - let modes = codecs[codecName].scalabilityModes || [] - modes = [...modes, ...codec.scalabilityModes] - codecs[codecName].scalabilityModes = [...new Set(modes)] - } - if (codec.channels) { - codecs[codecName].channels = codec.channels + if (isVideoCodec(codecName) || isAudioCodec(codecName)) { + codecs[codecName] = { ...codecs[codecName], mimeType: codec.mimeType } + if (codec.scalabilityModes) { + let modes = codecs[codecName].scalabilityModes || [] + modes = [...modes, ...codec.scalabilityModes] + codecs[codecName].scalabilityModes = [...new Set(modes)] + } + if (codec.channels) { + codecs[codecName].channels = codec.channels + } } } } - browserCapabilities.codecs = Object.keys(codecs).map((key) => { - return { codec: key, ...codecs[key] } as ICodecs + browserCapabilities.codecs = typedKeys(codecs).map((key) => { + return { codec: key, ...codecs[key] } }) } @@ -332,8 +336,13 @@ export default class PeerConnection extends EventEmitter { * Get sender tracks * @returns {Array} An array with all tracks in sender peer. */ - getTracks(): (MediaStreamTrack | null)[] { - return this.peer?.getSenders().map((sender: RTCRtpSender) => sender.track) || [] + getTracks(): MediaStreamTrack[] { + return ( + this.peer + ?.getSenders() + .map((sender: RTCRtpSender) => sender.track) + .filter((track) => track !== null) || [] + ) } /** @@ -397,7 +406,7 @@ export default class PeerConnection extends EventEmitter { const isMediaStreamValid = (mediaStream: MediaStream) => mediaStream.getAudioTracks().length <= 1 && mediaStream.getVideoTracks().length <= 1 -const getValidMediaStream = (mediaStream?: MediaStream | Array) => { +const getValidMediaStream = (mediaStream?: MediaStream | Array | null) => { if (!mediaStream) { return null } @@ -514,10 +523,11 @@ const addMediaStreamToPeer = (peer: RTCPeerConnection, mediaStream: MediaStream, if (track.kind === 'video') { initOptions.direction = !options.disableVideo ? 'sendonly' : 'inactive' - const encodings = [] + const encodings: RTCRtpEncodingParameters[] = [] if (options.scalabilityMode && new UserAgent().isChrome()) { logger.debug(`Video track with scalability mode: ${options.scalabilityMode}.`) + // Typescript dom RTCRtpEncodingParameters is not up to date with scaleabilityMode as as of Typescript 5.6.3 encodings.push({ scalabilityMode: options.scalabilityMode } as RTCRtpEncodingParameters) } else if (options.scalabilityMode) { logger.warn('SVC is only supported in Google Chrome') diff --git a/packages/millicast-sdk/src/Publish.ts b/packages/millicast-sdk/src/Publish.ts index ed8fce1c..b99d837c 100644 --- a/packages/millicast-sdk/src/Publish.ts +++ b/packages/millicast-sdk/src/Publish.ts @@ -294,7 +294,7 @@ export default class Publish extends BaseWebRTC { const publishPromise = this.signaling.publish(localSdp, this.options as SignalingPublishOptions) const setLocalDescriptionPromise = webRTCPeerInstance.peer?.setLocalDescription( - webRTCPeerInstance.sessionDescription as RTCSessionDescriptionInit + webRTCPeerInstance.sessionDescription ) promises = await Promise.all([publishPromise, setLocalDescriptionPromise]) let remoteSdp = promises[0] diff --git a/packages/millicast-sdk/src/types/PeerConnection.types.ts b/packages/millicast-sdk/src/types/PeerConnection.types.ts index c200b9a4..e2fe8a93 100644 --- a/packages/millicast-sdk/src/types/PeerConnection.types.ts +++ b/packages/millicast-sdk/src/types/PeerConnection.types.ts @@ -1,4 +1,4 @@ -import { VideoCodec } from './Codecs.types' +import { AudioCodec, VideoCodec } from './Codecs.types' export interface PeerConnectionConfig extends RTCConfiguration { /** @@ -32,8 +32,8 @@ export interface SdpOptions { } export interface ICodecs { - codec?: VideoCodec - mimeType: string + codec?: VideoCodec | AudioCodec + mimeType?: string scalabilityModes?: Array channels?: number } diff --git a/packages/millicast-sdk/src/types/Publish.types.ts b/packages/millicast-sdk/src/types/Publish.types.ts index cda19fed..1470af06 100644 --- a/packages/millicast-sdk/src/types/Publish.types.ts +++ b/packages/millicast-sdk/src/types/Publish.types.ts @@ -2,11 +2,11 @@ import { PublishServerEvent } from './BaseWebRTC.types' import { VideoCodec } from './Codecs.types' import { PeerConnectionConfig } from './PeerConnection.types' -export type PublishConnectOptions = { +export interface PublishConnectOptions { /** * - Source unique id. Only avialable if stream is multisource. */ - sourceId: string | null + sourceId?: string | null /** * - True to modify SDP for support stereo. Otherwise False. */ @@ -27,7 +27,7 @@ export type PublishConnectOptions = { * - MediaStream to offer in a stream. This object must have * 1 audio track and 1 video track, or at least one of them. Alternative you can provide both tracks in an array. */ - mediaStream: MediaStream | (MediaStreamTrack | null)[] | null + mediaStream: MediaStream | Array | null /** * - Broadcast bandwidth. 0 for unlimited. */ @@ -76,7 +76,7 @@ export type PublishConnectOptions = { /** * - Specify which events will be delivered by the server (any of "active" | "inactive" | "viewercount").* */ - events?: PublishServerEvent + events?: PublishServerEvent[] /** * - When multiple ingest streams are provided by the customer, add the ability to specify a priority between all ingest streams. Decimal integer between the range [-2^31, +2^31 - 1]. For more information, visit [our documentation](https://docs.dolby.io/streaming-apis/docs/backup-publishing). */ diff --git a/packages/millicast-sdk/src/utils/Codecs.ts b/packages/millicast-sdk/src/utils/Codecs.ts index d19adb06..30d58f13 100644 --- a/packages/millicast-sdk/src/utils/Codecs.ts +++ b/packages/millicast-sdk/src/utils/Codecs.ts @@ -1,6 +1,6 @@ /* eslint-disable no-new-wrappers */ /* eslint-disable camelcase */ -import { PictureParameterSet, SequenceParameterSet, VUIParameters } from '../types/Codecs.types' +import { AudioCodec, PictureParameterSet, SequenceParameterSet, VUIParameters } from '../types/Codecs.types' import BitStreamReader from './BitStreamReader' import { VideoCodec } from '../types/Codecs.types' import { SEIUserUnregisteredData } from '../types/View.types' @@ -705,3 +705,11 @@ export function addH26xSEI( encodedFrame.data = encodedFrameWithSEI } + +export function isVideoCodec(value: string): value is VideoCodec { + return Object.values(VideoCodec).includes(value as VideoCodec) +} + +export function isAudioCodec(value: string): value is AudioCodec { + return Object.values(AudioCodec).includes(value as AudioCodec) +} diff --git a/packages/millicast-sdk/src/utils/ObjectUtils.ts b/packages/millicast-sdk/src/utils/ObjectUtils.ts index 4fa08f39..04789c30 100644 --- a/packages/millicast-sdk/src/utils/ObjectUtils.ts +++ b/packages/millicast-sdk/src/utils/ObjectUtils.ts @@ -12,3 +12,7 @@ export function swapPropertyValues(obj: T): Array { + return Object.keys(obj) as Array +}