diff --git a/CHANGELOG.md b/CHANGELOG.md
index 86b4dfba3..1ba8693e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@ The Twilio Programmable Video SDKs use [Semantic Versioning](http://www.semver.o
**Version 1.x reached End of Life on September 8th, 2021.** See the changelog entry [here](https://www.twilio.com/changelog/end-of-life-complete-for-unsupported-versions-of-the-programmable-video-sdk). Support for the 1.x version ended on December 4th, 2020.
+2.26.1 (January 31, 2023)
+=========================
+
+Bug Fixes
+---------
+
+- Fixed a bug that manifests on Chrome versions 112+ where `Room.getStats()` rejects the returned Promise due to a
+ TypeError caused by trying to read from the now-removed `RTCMediaStreamTrackStats`. Instead, the SDK now reads from
+ the `RTCMediaSourceStats`. (VIDEO-12411)
+- Fixed an error in the type definition for the `attach()` method of `AudioTrack` and `VideoTrack`. (VIDEO-12242)
+- Fixed an error in the type definition for `createLocalAudioTrack()`. (VIDEO-12383)
+
2.26.0 (December 14, 2022)
==========================
diff --git a/README.md b/README.md
index 2fbe9d539..e90531dd5 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ Releases of twilio-video.js are hosted on a CDN, and you can include these
directly in your web app using a <script> tag.
```html
-
+
```
Using this method, twilio-video.js will set a browser global:
diff --git a/lib/webrtc/getstats.js b/lib/webrtc/getstats.js
index 453ba6572..68c4092ed 100644
--- a/lib/webrtc/getstats.js
+++ b/lib/webrtc/getstats.js
@@ -53,9 +53,7 @@ function _getStats(peerConnection, options) {
[remoteVideoTracks, 'remoteVideoTrackStats', true]
], ([tracks, statsArrayName, isRemote]) => {
return tracks.map(track => {
- return getTrackStats(peerConnection, track, Object.assign({
- isRemote: isRemote
- }, options)).then(trackStatsArray => {
+ return getTrackStats(peerConnection, track, Object.assign({ isRemote }, options)).then(trackStatsArray => {
trackStatsArray.forEach(trackStats => {
trackStats.trackId = track.id;
statsResponse[statsArrayName].push(trackStats);
@@ -470,7 +468,7 @@ function standardizeChromeOrSafariStats(response) {
}
});
- const isRemote = track && track.remoteSource;
+ const isRemote = track ? track.remoteSource : !localMedia;
const mainSources = isRemote ? [inbound] : outbound;
const stats = [];
const remoteSource = isRemote ? remoteOutbound : remoteInbound; // remote rtp stats
@@ -523,7 +521,7 @@ function standardizeChromeOrSafariStats(response) {
standardizedStats.frameWidthReceived = frameWidth;
} else {
standardizedStats.frameWidthSent = frameWidth;
- standardizedStats.frameWidthInput = track.frameWidth;
+ standardizedStats.frameWidthInput = track ? track.frameWidth : localMedia.width;
}
}
@@ -533,7 +531,7 @@ function standardizeChromeOrSafariStats(response) {
standardizedStats.frameHeightReceived = frameHeight;
} else {
standardizedStats.frameHeightSent = frameHeight;
- standardizedStats.frameHeightInput = track.frameHeight;
+ standardizedStats.frameHeightInput = track ? track.frameHeight : localMedia.height;
}
}
diff --git a/test/integration/spec/webrtc/getstats.js b/test/integration/spec/webrtc/getstats.js
index d090f411c..b77dd6c83 100644
--- a/test/integration/spec/webrtc/getstats.js
+++ b/test/integration/spec/webrtc/getstats.js
@@ -96,6 +96,10 @@ const sdpFormat = getSdpFormat();
assert(trackStats, `expected to have property: ${stats}.${trackType}[0]`);
[
+ { key: 'frameWidthInput', type: 'number' },
+ { key: 'frameHeightInput', type: 'number' },
+ { key: 'frameWidthSent', type: 'number' },
+ { key: 'frameHeightSent', type: 'number' },
{ key: 'ssrc', type: 'string' },
{ key: 'timestamp', type: 'number' },
{ key: 'bytesReceived', type: 'number' },
diff --git a/tsdef/AudioTrack.d.ts b/tsdef/AudioTrack.d.ts
index 7a89a9209..53e0b7f01 100644
--- a/tsdef/AudioTrack.d.ts
+++ b/tsdef/AudioTrack.d.ts
@@ -1,16 +1,5 @@
-import { Track } from './Track';
+import { MediaTrack } from './MediaTrack';
-export class AudioTrack extends Track {
- isStarted: boolean;
- isEnabled: boolean;
+export class AudioTrack extends MediaTrack {
kind: 'audio';
- mediaStreamTrack: MediaStreamTrack;
-
- attach(element?: HTMLMediaElement | string): HTMLMediaElement;
- detach(element?: HTMLMediaElement | string): HTMLMediaElement[];
-
- on(event: 'disabled', listener: (track: this) => void): this;
- on(event: 'enabled', listener: (track: this) => void): this;
- on(event: 'started', listener: (track: this) => void): this;
- on(event: string, listener: (...args: any[]) => void): this;
}
diff --git a/tsdef/MediaTrack.d.ts b/tsdef/MediaTrack.d.ts
new file mode 100644
index 000000000..d0d75d5ac
--- /dev/null
+++ b/tsdef/MediaTrack.d.ts
@@ -0,0 +1,16 @@
+import { Track } from './Track';
+
+export class MediaTrack extends Track {
+ isStarted: boolean;
+ isEnabled: boolean;
+ mediaStreamTrack: MediaStreamTrack;
+
+ attach(element?: HTMLMediaElement | string): HTMLMediaElement;
+ detach(element: HTMLMediaElement | string): HTMLMediaElement;
+ detach(): HTMLMediaElement[];
+
+ on(event: 'disabled', listener: (track: this) => void): this;
+ on(event: 'enabled', listener: (track: this) => void): this;
+ on(event: 'started', listener: (track: this) => void): this;
+ on(event: string, listener: (...args: any[]) => void): this;
+}
diff --git a/tsdef/VideoTrack.d.ts b/tsdef/VideoTrack.d.ts
index 7e169f4f6..2c419decc 100644
--- a/tsdef/VideoTrack.d.ts
+++ b/tsdef/VideoTrack.d.ts
@@ -1,4 +1,4 @@
-import { Track } from './Track';
+import { MediaTrack } from './MediaTrack';
import { VideoProcessor } from './VideoProcessor';
export namespace VideoTrack {
@@ -8,22 +8,12 @@ export namespace VideoTrack {
}
}
-export class VideoTrack extends Track {
- isStarted: boolean;
- isEnabled: boolean;
+export class VideoTrack extends MediaTrack {
dimensions: VideoTrack.Dimensions;
kind: 'video';
processor: VideoProcessor | null;
- mediaStreamTrack: MediaStreamTrack;
processedTrack: MediaStreamTrack | null;
addProcessor(processor: VideoProcessor): this;
removeProcessor(processor: VideoProcessor): this;
- attach(element?: HTMLMediaElement | string): HTMLVideoElement;
- detach(element?: HTMLMediaElement | string): HTMLVideoElement[];
-
- on(event: 'disabled', listener: (track: this) => void): this;
- on(event: 'enabled', listener: (track: this) => void): this;
- on(event: 'started', listener: (track: this) => void): this;
- on(event: string, listener: (...args: any[]) => void): this;
}
diff --git a/tsdef/index.d.ts b/tsdef/index.d.ts
index db94c6d1a..2def27966 100644
--- a/tsdef/index.d.ts
+++ b/tsdef/index.d.ts
@@ -12,7 +12,7 @@ export const isSupported: boolean;
export const version:string;
export const Logger: Log.RootLogger;
export function connect(token: string, options?: ConnectOptions): CancelablePromise;
-export function createLocalAudioTrack(options?: CreateLocalTracksOptions|CreateLocalAudioTrackOptions): Promise;
+export function createLocalAudioTrack(options?: CreateLocalTrackOptions|CreateLocalAudioTrackOptions): Promise;
export function createLocalTracks(options?: CreateLocalTracksOptions): Promise;
export function createLocalVideoTrack(options?: CreateLocalTrackOptions): Promise;
export function runPreflight(token: string, options?: PreflightOptions): PreflightTest;
@@ -43,6 +43,9 @@ export { Room } from './Room';
export { Track } from './Track';
export { TrackPublication } from './TrackPublication';
export { TwilioError } from './TwilioError';
+export { VideoProcessor } from './VideoProcessor';
+export { VideoTrack } from './VideoTrack';
+
export {
AudioCodec,
AudioCodecSettings,
@@ -93,5 +96,3 @@ export {
VideoTrackPublication,
VP8CodecSettings
} from './types';
-export { VideoProcessor } from './VideoProcessor';
-export { VideoTrack } from './VideoTrack';
diff --git a/tsdef/twilio-video-tests.ts b/tsdef/twilio-video-tests.ts
index ef3d65806..e354e1bf6 100644
--- a/tsdef/twilio-video-tests.ts
+++ b/tsdef/twilio-video-tests.ts
@@ -346,6 +346,9 @@ function trackSubscribed(track: Video.VideoTrack | Video.AudioTrack) {
}
function trackUnsubscribed(track: Video.VideoTrack | Video.AudioTrack) {
+ const element = document.createElement(track.kind);
+ track.detach('#foo').remove();
+ track.detach(element).remove();
track.detach().forEach(element => element.remove());
}