Skip to content

Commit

Permalink
Add degradationPreference option for LocalVideoTrack (#1138)
Browse files Browse the repository at this point in the history
* Add degradationPreference option for LocalVideoTrack

* Create dull-buckets-chew.md

* remove redundant check
  • Loading branch information
lukasIO authored May 15, 2024
1 parent f6e9d6a commit 760af83
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-buckets-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"livekit-client": patch
---

Add degradationPreference option for LocalVideoTrack
20 changes: 6 additions & 14 deletions src/room/participant/LocalParticipant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { trackPermissionToProto } from './ParticipantTrackPermission';
import {
computeTrackBackupEncodings,
computeVideoEncodings,
getDefaultDegradationPreference,
mediaTrackToLocalTrack,
} from './publishUtils';

Expand Down Expand Up @@ -857,6 +858,11 @@ export default class LocalParticipant extends Participant {

track.sender = await this.engine.createSender(track, opts, encodings);

if (track instanceof LocalVideoTrack) {
opts.degradationPreference ??= getDefaultDegradationPreference(track);
track.setDegradationPreference(opts.degradationPreference);
}

if (encodings) {
if (isFireFox() && track.kind === Track.Kind.Audio) {
/* Refer to RFC https://datatracker.ietf.org/doc/html/rfc7587#section-6.1,
Expand Down Expand Up @@ -889,20 +895,6 @@ export default class LocalParticipant extends Participant {
}
}

if (track.kind === Track.Kind.Video && track.source === Track.Source.ScreenShare) {
// a few of reasons we are forcing this setting without allowing overrides:
// 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue
// 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced`
try {
this.log.debug(`setting degradationPreference to maintain-resolution`);
const params = track.sender.getParameters();
params.degradationPreference = 'maintain-resolution';
await track.sender.setParameters(params);
} catch (e) {
this.log.warn(`failed to set degradationPreference: ${e}`);
}
}

await this.engine.negotiate();

if (track instanceof LocalVideoTrack) {
Expand Down
15 changes: 15 additions & 0 deletions src/room/participant/publishUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
isReactNative,
isSVCCodec,
isSafari,
unwrapConstraint,
} from '../utils';

/** @internal */
Expand Down Expand Up @@ -440,3 +441,17 @@ export class ScalabilityMode {
return `L${this.spatial}T${this.temporal}${this.suffix ?? ''}`;
}
}

export function getDefaultDegradationPreference(track: LocalVideoTrack): RTCDegradationPreference {
// a few of reasons we have different default paths:
// 1. without this, Chrome seems to aggressively resize the SVC video stating `quality-limitation: bandwidth` even when BW isn't an issue
// 2. since we are overriding contentHint to motion (to workaround L1T3 publishing), it overrides the default degradationPreference to `balanced`
if (
track.source === Track.Source.ScreenShare ||
(track.constraints.height && unwrapConstraint(track.constraints.height) >= 1080)
) {
return 'maintain-resolution';
} else {
return 'balanced';
}
}
11 changes: 10 additions & 1 deletion src/room/track/LocalTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ const defaultDimensionsTimeout = 1000;
export default abstract class LocalTrack<
TrackKind extends Track.Kind = Track.Kind,
> extends Track<TrackKind> {
protected _sender?: RTCRtpSender;

/** @internal */
sender?: RTCRtpSender;
get sender(): RTCRtpSender | undefined {
return this._sender;
}

/** @internal */
set sender(sender: RTCRtpSender | undefined) {
this._sender = sender;
}

/** @internal */
codec?: VideoCodec;
Expand Down
23 changes: 23 additions & 0 deletions src/room/track/LocalVideoTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
// a missing `getParameter` call.
private senderLock: Mutex;

private degradationPreference: RTCDegradationPreference = 'balanced';

override set sender(sender: RTCRtpSender | undefined) {
this._sender = sender;
if (this.degradationPreference) {
this.setDegradationPreference(this.degradationPreference);
}
}

/**
*
* @param mediaTrack
Expand Down Expand Up @@ -273,6 +282,20 @@ export default class LocalVideoTrack extends LocalTrack<Track.Kind.Video> {
}
}

async setDegradationPreference(preference: RTCDegradationPreference) {
this.degradationPreference = preference;
if (this.sender) {
try {
this.log.debug(`setting degradationPreference to ${preference}`, this.logContext);
const params = this.sender.getParameters();
params.degradationPreference = preference;
this.sender.setParameters(params);
} catch (e: any) {
this.log.warn(`failed to set degradationPreference`, { error: e, ...this.logContext });
}
}
}

addSimulcastTrack(
codec: VideoCodec,
encodings?: RTCRtpEncodingParameters[],
Expand Down
5 changes: 5 additions & 0 deletions src/room/track/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export interface TrackPublishDefaults {
*/
scalabilityMode?: ScalabilityMode;

/**
* degradation preference
*/
degradationPreference?: RTCDegradationPreference;

/**
* Up to two additional simulcast layers to publish in addition to the original
* Track.
Expand Down
6 changes: 4 additions & 2 deletions src/room/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,10 @@ export function isVideoCodec(maybeCodec: string): maybeCodec is VideoCodec {
return videoCodecs.includes(maybeCodec as VideoCodec);
}

export function unwrapConstraint(constraint: ConstrainDOMString): string {
if (typeof constraint === 'string') {
export function unwrapConstraint(constraint: ConstrainDOMString): string;
export function unwrapConstraint(constraint: ConstrainULong): number;
export function unwrapConstraint(constraint: ConstrainDOMString | ConstrainULong): string | number {
if (typeof constraint === 'string' || typeof constraint === 'number') {
return constraint;
}

Expand Down

0 comments on commit 760af83

Please sign in to comment.