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

@@ -6,7 +6,6 @@ import inboundVideoFramesFixtureJson from '../../fixtures/event_logs/inbound_vid
import participantsTracksFixtureJson from '../../fixtures/event_logs/participants_tracks.json';
import subscriptionQualityFixtureJson from '../../fixtures/event_logs/subscription_quality.json';
import watchedStreamsFixtureJson from '../../fixtures/event_logs/watched_streams.json';
import {translateVoiceEngineV2BridgeEventToEvents, translateVoiceEngineV2BridgeVideoFrameToEvent} from '../bridge';
import type {VoiceEngineV2Command} from '../protocol/commands';
import type {VoiceEngineV2Event} from '../protocol/events';
import type {
@@ -136,39 +135,45 @@ describe('voice engine v2 participants, subscriptions, and inbound video fixture
assertFixture(fixture);
});
it('translates native bridge participant, track, and frame events into v2 projections', () => {
let snapshot = createVoiceEngineV2InitialSnapshot(availableVoiceEngineV2Capabilities());
const bridgeEvents = translateVoiceEngineV2BridgeEventToEvents({
type: 'trackSubscribed',
payload: {
participantSid: 'PA_alice',
identity: 'alice',
participantName: 'Alice',
trackSid: 'TR_screen',
trackName: 'screen_share',
kind: 'video',
source: 'screen_share',
muted: false,
subscribed: true,
subscriptionStatus: 'subscribed',
it('projects a subscribed video track and its first frame into room and inbound-video state', () => {
const events: Array<VoiceEngineV2Event> = [
{
type: 'room.trackPublished',
track: {
participantIdentity: 'alice',
participantSid: 'PA_alice',
trackSid: 'TR_screen',
trackName: 'screen_share',
kind: 'video',
source: 'screen',
muted: false,
},
},
});
for (const event of bridgeEvents) {
snapshot = transitionVoiceEngineV2(snapshot, event).snapshot;
}
snapshot = transitionVoiceEngineV2(
snapshot,
translateVoiceEngineV2BridgeVideoFrameToEvent({
meta: {
{
type: 'inboundVideo.trackSubscribed',
track: {
participantSid: 'PA_alice',
participantIdentity: 'alice',
trackSid: 'TR_screen',
source: 'screen',
},
},
{
type: 'inboundVideo.frameReceived',
frame: {
participantSid: 'PA_alice',
trackSid: 'TR_screen',
width: 320,
height: 180,
timestampUs: 100,
byteLength: 86_400,
},
data: new ArrayBuffer(86_400),
}),
).snapshot;
},
];
let snapshot = createVoiceEngineV2InitialSnapshot(availableVoiceEngineV2Capabilities());
for (const event of events) {
snapshot = transitionVoiceEngineV2(snapshot, event).snapshot;
}
expect(snapshot.room.tracks.TR_screen).toMatchObject({
participantIdentity: 'alice',
@@ -121,94 +121,6 @@ describe('transitionVoiceEngineV2', () => {
]);
});
it('defers native microphone publish until the audio device module is ready', () => {
const queued = applyEvents(initialSnapshot(), [
{type: 'nativeAudioDeviceModule.statusChanged', status: 'warming'},
{type: 'microphone.publishRequested', options: {deviceId: 'default'}},
]);
const established = transitionVoiceEngineV2(queued, {
type: 'connection.externallyEstablished',
options: {url: 'wss://voice', token: ''},
});
expect(established.snapshot.connection.status).toBe('connected');
expect(established.snapshot.microphone.status).toBe('idle');
expect(established.snapshot.microphone.desired).toEqual({deviceId: 'default'});
expect(established.commands).toEqual([]);
const ready = transitionVoiceEngineV2(established.snapshot, {
type: 'nativeAudioDeviceModule.statusChanged',
status: 'ready',
});
expect(ready.snapshot.microphone.status).toBe('publishing');
expect(ready.commands).toEqual([{type: 'microphone.publish', operationId: 1, options: {deviceId: 'default'}}]);
});
it('fails deferred native microphone publish when the audio device module fails', () => {
const established = applyEvents(initialSnapshot(), [
{type: 'nativeAudioDeviceModule.statusChanged', status: 'warming'},
{type: 'microphone.publishRequested', options: {deviceId: 'default'}},
{type: 'connection.externallyEstablished', options: {url: 'wss://voice', token: ''}},
]);
const failed = transitionVoiceEngineV2(established, {
type: 'nativeAudioDeviceModule.statusChanged',
status: 'failed',
detail: 'ADM probe failed',
});
expect(failed.snapshot.microphone.status).toBe('failed');
expect(failed.snapshot.microphone.failure).toEqual({
code: 'deviceUnavailable',
capability: 'microphone',
message: 'ADM probe failed',
});
expect(failed.commands).toEqual([]);
});
it('does not republish an already published microphone on repeated ADM ready events', () => {
const established = applyEvents(initialSnapshot(), [
{type: 'nativeAudioDeviceModule.statusChanged', status: 'warming'},
{type: 'microphone.publishRequested', options: {deviceId: 'default'}},
{type: 'connection.externallyEstablished', options: {url: 'wss://voice', token: ''}},
]);
const ready = transitionVoiceEngineV2(established, {
type: 'nativeAudioDeviceModule.statusChanged',
status: 'ready',
});
const operationId = ready.commands[0]?.operationId ?? -1;
const published = transitionVoiceEngineV2(ready.snapshot, {
type: 'microphone.publishSucceeded',
operationId,
}).snapshot;
const repeated = transitionVoiceEngineV2(published, {
type: 'nativeAudioDeviceModule.statusChanged',
status: 'ready',
});
expect(repeated.snapshot.microphone.status).toBe('published');
expect(repeated.commands).toEqual([]);
});
it('does not let set-microphone-enabled implicitly publish while native ADM is warming', () => {
const established = applyEvents(initialSnapshot(), [
{type: 'nativeAudioDeviceModule.statusChanged', status: 'warming'},
{type: 'connection.externallyEstablished', options: {url: 'wss://voice', token: ''}},
]);
const enabled = transitionVoiceEngineV2(established, {
type: 'microphone.setEnabledRequested',
enabled: true,
});
expect(enabled.snapshot.microphone.enabled).toBe(true);
expect(enabled.snapshot.microphone.status).toBe('idle');
expect(enabled.commands).toEqual([]);
});
it('treats a repeated connection.externallyEstablished for the same endpoint as a no-op', () => {
const established = applyEvents(initialSnapshot(), [
{type: 'connection.externallyEstablished', options: {url: 'wss://voice', token: ''}},
@@ -17,7 +17,6 @@ import {transitionImplementation} from './implementation';
import {transitionInboundVideo} from './inboundVideo';
import {transitionLifecycle} from './lifecycle';
import {transitionMicrophone} from './microphone';
import {transitionNativeAudioDeviceModule} from './nativeAudioDeviceModule';
import {transitionNativeAudioTap} from './nativeAudioTap';
import {transitionNativeCapture} from './nativeCapture';
import {transitionNativeFrameSink} from './nativeFrameSink';
@@ -179,8 +178,6 @@ export function dispatchPlatformEvent(
case 'devices.selectAudioOutputRequested':
case 'devices.selectCameraRequested':
return transitionDevices(snapshot, event);
case 'nativeAudioDeviceModule.statusChanged':
return transitionNativeAudioDeviceModule(snapshot, event);
case 'nativeCapture.startRequested':
case 'nativeCapture.updateRequested':
case 'nativeCapture.stopRequested':
@@ -203,46 +203,6 @@ function microphoneOptionsWithAudioInputDevice(
return {...options, deviceId};
}
function nativeAudioDeviceModuleNotReadyError(snapshot: VoiceEngineV2Snapshot): VoiceEngineV2Error {
assert.ok(snapshot != null, 'nativeAudioDeviceModuleNotReadyError snapshot must not be null');
assert.ok(
snapshot.nativeAudioDeviceModule.status === 'failed',
'nativeAudioDeviceModuleNotReadyError requires failed ADM status',
);
return {
code: 'deviceUnavailable',
capability: 'microphone',
message: snapshot.nativeAudioDeviceModule.detail ?? 'Native audio device module failed to become ready',
};
}
function planNativeAudioDeviceModuleGatedMicrophone(snapshot: VoiceEngineV2Snapshot): VoiceEngineV2Transition | null {
assert.ok(snapshot != null, 'planNativeAudioDeviceModuleGatedMicrophone snapshot must not be null');
assert.ok(snapshot.nativeAudioDeviceModule != null, 'snapshot.nativeAudioDeviceModule must not be null');
switch (snapshot.nativeAudioDeviceModule.status) {
case 'unsupported':
case 'ready':
return null;
case 'unknown':
case 'warming':
return {
snapshot: {...snapshot, microphone: {...snapshot.microphone, status: 'idle', failure: null}},
commands: [],
};
case 'failed': {
const error = nativeAudioDeviceModuleNotReadyError(snapshot);
return {
snapshot: {
...snapshot,
microphone: {...snapshot.microphone, status: 'failed', published: null, operationId: null, failure: error},
lastFailure: error,
},
commands: [],
};
}
}
}
export function planMicrophoneDeviceChange(
snapshot: VoiceEngineV2Snapshot,
deviceId: string | null,
@@ -327,8 +287,6 @@ export function beginMicrophonePublish(snapshot: VoiceEngineV2Snapshot): VoiceEn
};
}
if (!isConnected(snapshot)) return {snapshot, commands: []};
const gated = planNativeAudioDeviceModuleGatedMicrophone(snapshot);
if (gated) return gated;
if (snapshot.microphone.status === 'publishing') return {snapshot, commands: []};
if (snapshot.microphone.status === 'published' && sameMicrophoneOptions(snapshot.microphone.published, desired)) {
return {snapshot, commands: []};
@@ -1,29 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import type {VoiceEngineV2Event} from '../../protocol/events';
import type {VoiceEngineV2Snapshot, VoiceEngineV2Transition} from '../state';
import {beginMicrophonePublish} from './microphone';
type VoiceEngineV2NativeAudioDeviceModuleEvent = Extract<
VoiceEngineV2Event,
{type: 'nativeAudioDeviceModule.statusChanged'}
>;
export function transitionNativeAudioDeviceModule(
snapshot: VoiceEngineV2Snapshot,
event: VoiceEngineV2NativeAudioDeviceModuleEvent,
): VoiceEngineV2Transition {
assert.ok(snapshot != null, 'transitionNativeAudioDeviceModule snapshot must not be null');
assert.ok(event != null, 'transitionNativeAudioDeviceModule event must not be null');
assert.equal(event.type, 'nativeAudioDeviceModule.statusChanged', 'unexpected ADM event type');
assert.equal(typeof event.status, 'string', 'ADM status must be a string');
const next: VoiceEngineV2Snapshot = {
...snapshot,
nativeAudioDeviceModule: {
status: event.status,
detail: event.detail ?? null,
},
};
return beginMicrophonePublish(next);
}
@@ -27,7 +27,6 @@ import type {
VoiceEngineV2LiveKitRoomState,
VoiceEngineV2MediaStatus,
VoiceEngineV2MicrophoneOptions,
VoiceEngineV2NativeAudioDeviceModuleState,
VoiceEngineV2NativeAudioTapOptions,
VoiceEngineV2NativeCaptureOptions,
VoiceEngineV2NativeFrameSinkOptions,
@@ -162,7 +161,6 @@ export interface VoiceEngineV2Snapshot {
connection: VoiceEngineV2ConnectionState;
gateway: VoiceEngineV2GatewayState;
liveKit: VoiceEngineV2LiveKitState;
nativeAudioDeviceModule: VoiceEngineV2NativeAudioDeviceModuleState;
microphone: VoiceEngineV2MicrophoneState;
camera: VoiceEngineV2LocalMediaState<VoiceEngineV2CameraOptions>;
screen: VoiceEngineV2LocalMediaState<VoiceEngineV2ScreenOptions>;
@@ -286,10 +284,6 @@ export function createVoiceEngineV2InitialSnapshot(
serverRegion: null,
failure: null,
},
nativeAudioDeviceModule: {
status: 'unsupported',
detail: null,
},
microphone: {
status: 'idle',
desired: null,