livekit-client
Advanced tools
Comparing version 0.9.7 to 0.10.0
import 'webrtc-adapter'; | ||
import { ParticipantInfo, TrackType } from '../proto/livekit_models'; | ||
import { JoinResponse, SignalRequest, SignalTarget, SpeakerInfo, TrackPublishedResponse, UpdateSubscription, UpdateTrackSettings } from '../proto/livekit_rtc'; | ||
import { JoinResponse, SignalRequest, SignalTarget, SpeakerInfo, TrackPublishedResponse, UpdateSubscription, UpdateTrackSettings, VideoQuality } from '../proto/livekit_rtc'; | ||
import { Track } from '../room/track/Track'; | ||
@@ -28,2 +28,3 @@ interface ConnectOpts { | ||
sendUpdateSubscription(sub: UpdateSubscription): void; | ||
sendSetSimulcastLayers(sid: Track.SID, layers: VideoQuality[]): void; | ||
sendLeave(): void; | ||
@@ -66,2 +67,3 @@ close(): void; | ||
sendUpdateSubscription(sub: UpdateSubscription): void; | ||
sendSetSimulcastLayers(sid: Track.SID, layers: VideoQuality[]): void; | ||
sendLeave(): void; | ||
@@ -68,0 +70,0 @@ sendRequest(req: SignalRequest): void; |
@@ -170,2 +170,10 @@ "use strict"; | ||
} | ||
sendSetSimulcastLayers(sid, layers) { | ||
this.sendRequest({ | ||
simulcast: { | ||
trackSid: sid, | ||
layers, | ||
}, | ||
}); | ||
} | ||
sendLeave() { | ||
@@ -172,0 +180,0 @@ this.sendRequest(livekit_rtc_1.SignalRequest.fromPartial({ leave: {} })); |
@@ -105,2 +105,3 @@ "use strict"; | ||
const publication = new LocalTrackPublication_1.default(track.kind, ti, track); | ||
track.sid = ti.sid; | ||
let encodings; | ||
@@ -132,6 +133,6 @@ // for video | ||
if (track instanceof LocalVideoTrack_1.default) { | ||
track.startMonitor(); | ||
track.startMonitor(this.engine.client); | ||
} | ||
if (options === null || options === void 0 ? void 0 : options.videoCodec) { | ||
this.setPreferredCodec(transceiver, track.kind, options === null || options === void 0 ? void 0 : options.videoCodec); | ||
this.setPreferredCodec(transceiver, track.kind, options.videoCodec); | ||
} | ||
@@ -282,3 +283,3 @@ this.addTrackPublication(publication); | ||
} | ||
setPreferredCodec(transceiver, kind, videoCodec = 'vp8') { | ||
setPreferredCodec(transceiver, kind, videoCodec) { | ||
if (!('getCapabilities' in RTCRtpSender)) { | ||
@@ -285,0 +286,0 @@ return; |
@@ -0,11 +1,20 @@ | ||
import { SignalClient } from '../../api/SignalClient'; | ||
import { VideoQuality } from '../../proto/livekit_rtc'; | ||
import { VideoSenderStats } from '../stats'; | ||
import LocalTrack from './LocalTrack'; | ||
export default class LocalVideoTrack extends LocalTrack { | ||
signalClient?: SignalClient; | ||
private prevStats?; | ||
private numDowngrades; | ||
private numSuccesses; | ||
private disableCount; | ||
private encodings?; | ||
constructor(mediaTrack: MediaStreamTrack, name?: string, constraints?: MediaTrackConstraints); | ||
startMonitor(): void; | ||
get isSimulcast(): boolean; | ||
startMonitor(signalClient: SignalClient): void; | ||
stop(): void; | ||
getSenderStats(): Promise<VideoSenderStats[]>; | ||
setPublishingQuality(maxQuality: VideoQuality): void; | ||
private monitorSender; | ||
private handleStats; | ||
} |
@@ -16,5 +16,13 @@ "use strict"; | ||
const loglevel_1 = __importDefault(require("loglevel")); | ||
const livekit_rtc_1 = require("../../proto/livekit_rtc"); | ||
const stats_1 = require("../stats"); | ||
const LocalTrack_1 = __importDefault(require("./LocalTrack")); | ||
const Track_1 = require("./Track"); | ||
// number of downgrades to turn off highest quality layer | ||
const DOWNGRADES_TO_STEP_DOWN = 2; | ||
// number of successes to turn on next higher layer | ||
const SUCCESSES_TO_STEP_UP = 4; | ||
// once it's disabled this number of times, it will be turned off for the rest | ||
// of the session | ||
const MAX_QUALITY_ATTEMPTS = 3; | ||
const ridOrder = ['f', 'h', 'q']; | ||
@@ -24,3 +32,15 @@ class LocalVideoTrack extends LocalTrack_1.default { | ||
super(mediaTrack, Track_1.Track.Kind.Video, name); | ||
// simulcast controls | ||
// consecutive downgrades, reset when we have a success | ||
this.numDowngrades = 0; | ||
// consecutive successes, reset when we have a downgrade | ||
this.numSuccesses = 0; | ||
// keep track of times we had to disable a track | ||
this.disableCount = { | ||
2: 0, | ||
1: 0, | ||
0: 0, | ||
}; | ||
this.monitorSender = () => __awaiter(this, void 0, void 0, function* () { | ||
var _a; | ||
if (!this.sender) { | ||
@@ -30,14 +50,47 @@ return; | ||
const stats = yield this.getSenderStats(); | ||
if (this.prevStats) { | ||
if (this.prevStats.length !== stats.length) { | ||
// can't compare if length different | ||
loglevel_1.default.warn('number of tracks changed', stats); | ||
return; | ||
const statsMap = new Map(stats.map((s) => [s.rid, s])); | ||
const params = this.sender.getParameters(); | ||
this.encodings = params.encodings; | ||
let bestEncoding; | ||
this.encodings.forEach((encoding) => { | ||
// skip inactive encodings | ||
if (bestEncoding === undefined && encoding.active) { | ||
bestEncoding = encoding; | ||
} | ||
for (let i = 0; i < this.prevStats.length; i += 1) { | ||
this.handleStats(this.prevStats[i], stats[i]); | ||
}); | ||
if (!bestEncoding) { | ||
return; | ||
} | ||
const rid = (_a = bestEncoding.rid) !== null && _a !== void 0 ? _a : ''; | ||
const sendStats = statsMap.get(rid); | ||
if (!sendStats) { | ||
return; | ||
} | ||
const isLimited = sendStats.qualityLimitationReason === 'bandwidth' || sendStats.qualityLimitationReason === 'cpu'; | ||
if (isLimited) { | ||
this.numDowngrades += 1; | ||
this.numSuccesses = 0; | ||
} | ||
else { | ||
this.numSuccesses += 1; | ||
this.numDowngrades = 0; | ||
} | ||
const currentQuality = videoQualityForRid(rid); | ||
if (currentQuality === livekit_rtc_1.VideoQuality.UNRECOGNIZED) { | ||
return; | ||
} | ||
let nextQuality = currentQuality; | ||
if (this.numDowngrades > DOWNGRADES_TO_STEP_DOWN && currentQuality > livekit_rtc_1.VideoQuality.LOW) { | ||
this.disableCount[currentQuality] += 1; | ||
nextQuality = currentQuality - 1; | ||
} | ||
else if (this.numSuccesses > SUCCESSES_TO_STEP_UP && currentQuality < livekit_rtc_1.VideoQuality.HIGH) { | ||
if (this.disableCount[currentQuality + 1] <= MAX_QUALITY_ATTEMPTS) { | ||
nextQuality = currentQuality + 1; | ||
} | ||
} | ||
// compare and findout | ||
this.prevStats = stats; | ||
if (nextQuality !== currentQuality) { | ||
this.setPublishingQuality(nextQuality); | ||
} | ||
// this.prevStats = stats; | ||
setTimeout(() => { | ||
@@ -49,3 +102,16 @@ this.monitorSender(); | ||
} | ||
startMonitor() { | ||
get isSimulcast() { | ||
var _a; | ||
if (((_a = this.sender) === null || _a === void 0 ? void 0 : _a.getParameters().encodings.length) === 3) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/* internal */ | ||
startMonitor(signalClient) { | ||
// only monitor simulcast streams | ||
if (!this.isSimulcast) { | ||
return; | ||
} | ||
this.signalClient = signalClient; | ||
setTimeout(() => { | ||
@@ -115,2 +181,32 @@ this.monitorSender(); | ||
} | ||
setPublishingQuality(maxQuality) { | ||
var _a; | ||
if (!this.isSimulcast || !this.encodings) { | ||
return; | ||
} | ||
let hasChanged = false; | ||
this.encodings.forEach((encoding) => { | ||
var _a; | ||
const quality = videoQualityForRid((_a = encoding.rid) !== null && _a !== void 0 ? _a : ''); | ||
const active = quality <= maxQuality; | ||
if (active !== encoding.active) { | ||
hasChanged = true; | ||
encoding.active = active; | ||
} | ||
}); | ||
if (!hasChanged || !this.sender || !this.sid) { | ||
return; | ||
} | ||
this.numDowngrades = 0; | ||
this.numSuccesses = 0; | ||
const params = this.sender.getParameters(); | ||
params.encodings = this.encodings; | ||
loglevel_1.default.debug('setting publishing quality. max quality', maxQuality, 'encodings', params.encodings); | ||
this.sender.setParameters(params); | ||
const layers = []; | ||
for (let q = livekit_rtc_1.VideoQuality.LOW; q <= maxQuality; q += 1) { | ||
layers.push(q); | ||
} | ||
(_a = this.signalClient) === null || _a === void 0 ? void 0 : _a.sendSetSimulcastLayers(this.sid, layers); | ||
} | ||
handleStats(prev, curr) { | ||
@@ -126,2 +222,14 @@ const pliDelta = curr.pliCount - prev.pliCount; | ||
exports.default = LocalVideoTrack; | ||
function videoQualityForRid(rid) { | ||
switch (rid) { | ||
case 'f': | ||
return livekit_rtc_1.VideoQuality.HIGH; | ||
case 'h': | ||
return livekit_rtc_1.VideoQuality.MEDIUM; | ||
case 'q': | ||
return livekit_rtc_1.VideoQuality.LOW; | ||
default: | ||
return livekit_rtc_1.VideoQuality.UNRECOGNIZED; | ||
} | ||
} | ||
//# sourceMappingURL=LocalVideoTrack.js.map |
@@ -1,2 +0,2 @@ | ||
export declare const version = "0.9.7"; | ||
export declare const version = "0.10.0"; | ||
export declare const protocolVersion = 2; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.protocolVersion = exports.version = void 0; | ||
exports.version = '0.9.7'; | ||
exports.version = '0.10.0'; | ||
exports.protocolVersion = 2; | ||
//# sourceMappingURL=version.js.map |
{ | ||
"name": "livekit-client", | ||
"version": "0.9.7", | ||
"version": "0.10.0", | ||
"description": "JavaScript/TypeScript client SDK for LiveKit", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
456597
7189