refactor(voice): drop the native only surface from voice_engine_v2 and narrow the desktop bridge (#1788)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-20 17:44:37 +02:00
1 parent 9731bac40f
commit 4f21430880
43 files changed
+196 -3682

No files matched your search

@@ -1,29 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {bench, describe} from 'vitest';
import type {VoiceEngineV2InboundVideoFrame} from '../protocol/types';
import {VoiceEngineV2FrameStatsAccumulator} from './frameStatsBatching';
const frame: VoiceEngineV2InboundVideoFrame = Object.freeze({
participantSid: 'sid-remote',
participantIdentity: 'identity-remote',
trackSid: 'track-video-1',
width: 1280,
height: 720,
timestampUs: 1_000_000,
byteLength: 320_000,
});
const recordAccumulator = new VoiceEngineV2FrameStatsAccumulator();
const flushAccumulator = new VoiceEngineV2FrameStatsAccumulator();
describe('voice engine v2 frame stats accumulator hot paths', () => {
bench('record per-frame update', () => {
recordAccumulator.record(frame);
});
bench('record then flush one dirty track', () => {
flushAccumulator.record(frame);
flushAccumulator.flushDirty(() => {});
});
});
@@ -1,129 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {describe, expect, it} from 'vitest';
import type {VoiceEngineV2InboundVideoFrame} from '../protocol/types';
import {
VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP,
VoiceEngineV2FrameStatsAccumulator,
type VoiceEngineV2FrameStatsEvent,
} from './frameStatsBatching';
function makeFrame(overrides: Partial<VoiceEngineV2InboundVideoFrame> = {}): VoiceEngineV2InboundVideoFrame {
return {
participantSid: 'PA_alice',
participantIdentity: 'alice',
trackSid: 'TR_screen',
width: 1280,
height: 720,
timestampUs: 1000,
byteLength: 1_382_400,
...overrides,
};
}
function collectFlush(accumulator: VoiceEngineV2FrameStatsAccumulator): Array<VoiceEngineV2FrameStatsEvent> {
const events: Array<VoiceEngineV2FrameStatsEvent> = [];
accumulator.flushDirty((event) => events.push(event));
return events;
}
describe('voice engine v2 frame stats accumulator', () => {
it('accumulates a cumulative frame count and flushes one absolute event per dirty track', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
for (let index = 1; index <= 30; index += 1) {
expect(accumulator.record(makeFrame({timestampUs: index * 33_333}))).toBe(true);
}
expect(accumulator.trackedTracksCount).toBe(1);
expect(accumulator.dirtyTracksCount).toBe(1);
const events = collectFlush(accumulator);
expect(events).toEqual([
{
type: 'inboundVideo.frameStats',
stats: {
participantSid: 'PA_alice',
participantIdentity: 'alice',
trackSid: 'TR_screen',
width: 1280,
height: 720,
frameCount: 30,
lastFrameTimestampUs: 30 * 33_333,
lastFrameByteLength: 1_382_400,
},
},
]);
expect(accumulator.dirtyTracksCount).toBe(0);
expect(collectFlush(accumulator)).toEqual([]);
});
it('keeps the cumulative count across flushes and reuses the same record without re-allocating slots', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
accumulator.record(makeFrame({timestampUs: 1000}));
collectFlush(accumulator);
accumulator.record(makeFrame({timestampUs: 2000, width: 640, height: 360, byteLength: undefined}));
const events = collectFlush(accumulator);
expect(events).toHaveLength(1);
expect(events[0]?.stats).toMatchObject({
frameCount: 2,
lastFrameTimestampUs: 2000,
lastFrameByteLength: null,
width: 640,
height: 360,
});
expect(accumulator.trackedTracksCount).toBe(1);
});
it('omits participantIdentity when never observed for the track', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
accumulator.record(makeFrame({participantIdentity: undefined}));
const events = collectFlush(accumulator);
expect(events).toHaveLength(1);
expect(events[0]?.stats).not.toHaveProperty('participantIdentity');
});
it('drops updates for new tracks at the cap and counts the drops', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
for (let index = 0; index < VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP; index += 1) {
expect(accumulator.record(makeFrame({trackSid: `TR_${index}`}))).toBe(true);
}
expect(accumulator.trackedTracksCount).toBe(VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP);
expect(accumulator.record(makeFrame({trackSid: 'TR_overflow'}))).toBe(false);
expect(accumulator.droppedUpdatesCount).toBe(1);
expect(accumulator.record(makeFrame({trackSid: 'TR_0'}))).toBe(true);
expect(accumulator.trackedTracksCount).toBe(VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP);
accumulator.removeTrack('TR_1');
expect(accumulator.record(makeFrame({trackSid: 'TR_overflow'}))).toBe(true);
expect(accumulator.trackedTracksCount).toBe(VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP);
});
it('removes dirty bookkeeping when a tracked track is removed before a flush', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
accumulator.record(makeFrame({trackSid: 'TR_a'}));
accumulator.record(makeFrame({trackSid: 'TR_b'}));
expect(accumulator.dirtyTracksCount).toBe(2);
accumulator.removeTrack('TR_a');
expect(accumulator.dirtyTracksCount).toBe(1);
const events = collectFlush(accumulator);
expect(events).toHaveLength(1);
expect(events[0]?.stats.trackSid).toBe('TR_b');
});
it('clear resets records, dirty bookkeeping, and keeps the dropped counter', () => {
const accumulator = new VoiceEngineV2FrameStatsAccumulator();
for (let index = 0; index <= VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP; index += 1) {
accumulator.record(makeFrame({trackSid: `TR_${index}`}));
}
expect(accumulator.droppedUpdatesCount).toBe(1);
accumulator.clear();
expect(accumulator.trackedTracksCount).toBe(0);
expect(accumulator.dirtyTracksCount).toBe(0);
expect(accumulator.droppedUpdatesCount).toBe(1);
expect(collectFlush(accumulator)).toEqual([]);
});
});
@@ -1,129 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import type {VoiceEngineV2Event} from '../protocol/events';
import type {VoiceEngineV2InboundVideoFrame} from '../protocol/types';
import {VOICE_ENGINE_V2_COALESCED_TRACKS_CAP} from './frameCoalescing';
export const VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP = VOICE_ENGINE_V2_COALESCED_TRACKS_CAP;
export const VOICE_ENGINE_V2_FRAME_STATS_FLUSH_INTERVAL_MS = 1000;
export type VoiceEngineV2FrameStatsEvent = Extract<VoiceEngineV2Event, {type: 'inboundVideo.frameStats'}>;
interface VoiceEngineV2FrameStatsRecord {
participantSid: string;
participantIdentity: string | null;
trackSid: string;
width: number;
height: number;
frameCount: number;
lastFrameTimestampUs: number;
lastFrameByteLength: number | null;
dirty: boolean;
}
export class VoiceEngineV2FrameStatsAccumulator {
private readonly recordsByTrack = new Map<string, VoiceEngineV2FrameStatsRecord>();
private droppedUpdatesCountValue = 0;
private dirtyTracksCountValue = 0;
record(frame: VoiceEngineV2InboundVideoFrame): boolean {
assert.ok(frame != null, 'record requires a non-null frame');
assert.equal(typeof frame.trackSid, 'string', 'record requires a string trackSid');
let entry = this.recordsByTrack.get(frame.trackSid);
if (entry === undefined) {
if (this.recordsByTrack.size >= VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP) {
this.droppedUpdatesCountValue += 1;
assert.ok(this.droppedUpdatesCountValue >= 1, 'dropped updates count must stay positive after a drop');
return false;
}
entry = {
participantSid: frame.participantSid,
participantIdentity: frame.participantIdentity ?? null,
trackSid: frame.trackSid,
width: frame.width,
height: frame.height,
frameCount: 0,
lastFrameTimestampUs: frame.timestampUs,
lastFrameByteLength: frame.byteLength ?? null,
dirty: false,
};
this.recordsByTrack.set(frame.trackSid, entry);
assert.ok(this.recordsByTrack.size <= VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP, 'frame stats records exceeded cap');
}
entry.participantSid = frame.participantSid;
entry.participantIdentity = frame.participantIdentity ?? entry.participantIdentity;
entry.width = frame.width;
entry.height = frame.height;
entry.frameCount += 1;
entry.lastFrameTimestampUs = frame.timestampUs;
entry.lastFrameByteLength = frame.byteLength ?? null;
if (!entry.dirty) {
entry.dirty = true;
this.dirtyTracksCountValue += 1;
}
assert.ok(this.dirtyTracksCountValue <= this.recordsByTrack.size, 'dirty count must not exceed tracked records');
return true;
}
flushDirty(emit: (event: VoiceEngineV2FrameStatsEvent) => void): number {
assert.equal(typeof emit, 'function', 'flushDirty requires a function emitter');
assert.ok(this.dirtyTracksCountValue >= 0, 'dirty count must be non-negative before flush');
let flushedCount = 0;
for (const entry of this.recordsByTrack.values()) {
if (!entry.dirty) continue;
entry.dirty = false;
flushedCount += 1;
emit({
type: 'inboundVideo.frameStats',
stats: {
participantSid: entry.participantSid,
...(entry.participantIdentity !== null ? {participantIdentity: entry.participantIdentity} : {}),
trackSid: entry.trackSid,
width: entry.width,
height: entry.height,
frameCount: entry.frameCount,
lastFrameTimestampUs: entry.lastFrameTimestampUs,
lastFrameByteLength: entry.lastFrameByteLength,
},
});
}
assert.equal(flushedCount, this.dirtyTracksCountValue, 'flush must visit every dirty record exactly once');
this.dirtyTracksCountValue = 0;
return flushedCount;
}
removeTrack(trackSid: string): void {
assert.equal(typeof trackSid, 'string', 'removeTrack requires a string trackSid');
assert.ok(trackSid.length > 0, 'removeTrack requires a non-empty trackSid');
const entry = this.recordsByTrack.get(trackSid);
if (entry === undefined) return;
if (entry.dirty) {
this.dirtyTracksCountValue -= 1;
assert.ok(this.dirtyTracksCountValue >= 0, 'dirty count must stay non-negative after removal');
}
this.recordsByTrack.delete(trackSid);
}
clear(): void {
this.recordsByTrack.clear();
this.dirtyTracksCountValue = 0;
assert.equal(this.recordsByTrack.size, 0, 'clear must remove every tracked record');
assert.equal(this.dirtyTracksCountValue, 0, 'clear must reset the dirty count');
}
get trackedTracksCount(): number {
assert.ok(this.recordsByTrack.size <= VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP, 'frame stats records exceeded cap');
return this.recordsByTrack.size;
}
get dirtyTracksCount(): number {
assert.ok(this.dirtyTracksCountValue >= 0, 'dirty count must be non-negative');
return this.dirtyTracksCountValue;
}
get droppedUpdatesCount(): number {
assert.ok(this.droppedUpdatesCountValue >= 0, 'dropped updates count must be non-negative');
return this.droppedUpdatesCountValue;
}
}
@@ -15,12 +15,6 @@ export {
isVoiceEngineV2FrameReceivedEvent,
VOICE_ENGINE_V2_COALESCED_TRACKS_CAP,
} from './frameCoalescing';
export type {VoiceEngineV2FrameStatsEvent} from './frameStatsBatching';
export {
VOICE_ENGINE_V2_FRAME_STATS_FLUSH_INTERVAL_MS,
VOICE_ENGINE_V2_FRAME_STATS_TRACKS_CAP,
VoiceEngineV2FrameStatsAccumulator,
} from './frameStatsBatching';
export type {
VoiceEngineV2ClockPort,
VoiceEngineV2EntropySource,