Skip to content

Commit

Permalink
Wait for dimensions to become available prior to publishing (livekit#543
Browse files Browse the repository at this point in the history
)

Video dimensions are required for simulcast publishing to take place.
When certain transformations are applied to the MediaStreamTrack, it could
take a bit of time for getSettings() to return the correct dimensions.

Currently this will fallback to the old behavior. In future versions,
we could make dimensions a requirement.
  • Loading branch information
davidzhao authored Jan 2, 2023
1 parent db8afd8 commit fc6a015
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-houses-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'livekit-client': patch
---

Wait for dimensions to become available prior to publishing
22 changes: 14 additions & 8 deletions src/room/participant/LocalParticipant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,13 +528,19 @@ export default class LocalParticipant extends Participant {
let encodings: RTCRtpEncodingParameters[] | undefined;
let simEncodings: RTCRtpEncodingParameters[] | undefined;
if (track.kind === Track.Kind.Video) {
// TODO: support react native, which doesn't expose getSettings
const settings = track.mediaStreamTrack.getSettings();
const width = settings.width ?? track.dimensions?.width;
const height = settings.height ?? track.dimensions?.height;
let dims: Track.Dimensions = {
width: 0,
height: 0,
};
try {
dims = await track.waitForDimensions();
} catch (e) {
// log failure
log.error('could not determine track dimensions');
}
// width and height should be defined for video
req.width = width ?? 0;
req.height = height ?? 0;
req.width = dims.width;
req.height = dims.height;
// for svc codecs, disable simulcast and use vp8 for backup codec
if (track instanceof LocalVideoTrack) {
if (opts?.videoCodec === 'av1') {
Expand Down Expand Up @@ -565,8 +571,8 @@ export default class LocalParticipant extends Participant {

encodings = computeVideoEncodings(
track.source === Track.Source.ScreenShare,
width,
height,
dims.width,
dims.height,
opts,
);
req.layers = videoLayersFromEncodings(req.width, req.height, simEncodings ?? encodings);
Expand Down
20 changes: 19 additions & 1 deletion src/room/track/LocalTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import log from '../../logger';
import DeviceManager from '../DeviceManager';
import { TrackInvalidError } from '../errors';
import { TrackEvent } from '../events';
import { getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isMobile } from '../utils';
import { getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isMobile, sleep } from '../utils';
import type { VideoCodec } from './options';
import { attachToElement, detachTrack, Track } from './Track';

const defaultDimensionsTimeout = 2 * 1000;

export default abstract class LocalTrack extends Track {
/** @internal */
sender?: RTCRtpSender;
Expand Down Expand Up @@ -72,6 +74,22 @@ export default abstract class LocalTrack extends Track {
return this.providedByUser;
}

async waitForDimensions(timeout = defaultDimensionsTimeout): Promise<Track.Dimensions> {
if (this.kind === Track.Kind.Audio) {
throw new Error('cannot get dimensions for audio tracks');
}

const started = Date.now();
while (Date.now() - started < timeout) {
const dims = this.dimensions;
if (dims) {
return dims;
}
await sleep(50);
}
throw new TrackInvalidError('unable to get track dimensions after timeout');
}

/**
* @returns DeviceID of the device that is currently being used for this track
*/
Expand Down

0 comments on commit fc6a015

Please sign in to comment.